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_rows = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7116         
7117         if(rows.length != h_rows.length) {
7118             Roo.log(h_rows);
7119             return;
7120         }
7121         
7122         for(var i = 0; i < rows.length; i++) {
7123             
7124             for(var j = 0; w.length; j++) {
7125                 
7126                 var size_cls = w[j].split("-");
7127                 
7128                 if(!Number.isInteger(size_cls[1] * 1)) {
7129                     continue;
7130                 }
7131                 
7132                 if(!this.colModel.config[col_index][size_cls[0]]) {
7133                     continue;
7134                 }
7135                 
7136                 rows[i].classList.replace(
7137                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7138                     "col-"+size_cls[0]+"-"+size_cls[1]
7139                 );
7140                 
7141                 h_rows[i].classList.replace(
7142                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7143                     "col-"+size_cls[0]+"-"+size_cls[1]
7144                 );
7145                 
7146                 this.colModel.config[col_index][size_cls[0]] = size_cls[1]
7147             }
7148         }
7149     }
7150 });
7151
7152  
7153
7154  /*
7155  * - LGPL
7156  *
7157  * table cell
7158  * 
7159  */
7160
7161 /**
7162  * @class Roo.bootstrap.TableCell
7163  * @extends Roo.bootstrap.Component
7164  * Bootstrap TableCell class
7165  * @cfg {String} html cell contain text
7166  * @cfg {String} cls cell class
7167  * @cfg {String} tag cell tag (td|th) default td
7168  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7169  * @cfg {String} align Aligns the content in a cell
7170  * @cfg {String} axis Categorizes cells
7171  * @cfg {String} bgcolor Specifies the background color of a cell
7172  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7173  * @cfg {Number} colspan Specifies the number of columns a cell should span
7174  * @cfg {String} headers Specifies one or more header cells a cell is related to
7175  * @cfg {Number} height Sets the height of a cell
7176  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7177  * @cfg {Number} rowspan Sets the number of rows a cell should span
7178  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7179  * @cfg {String} valign Vertical aligns the content in a cell
7180  * @cfg {Number} width Specifies the width of a cell
7181  * 
7182  * @constructor
7183  * Create a new TableCell
7184  * @param {Object} config The config object
7185  */
7186
7187 Roo.bootstrap.TableCell = function(config){
7188     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7189 };
7190
7191 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7192     
7193     html: false,
7194     cls: false,
7195     tag: false,
7196     abbr: false,
7197     align: false,
7198     axis: false,
7199     bgcolor: false,
7200     charoff: false,
7201     colspan: false,
7202     headers: false,
7203     height: false,
7204     nowrap: false,
7205     rowspan: false,
7206     scope: false,
7207     valign: false,
7208     width: false,
7209     
7210     
7211     getAutoCreate : function(){
7212         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7213         
7214         cfg = {
7215             tag: 'td'
7216         };
7217         
7218         if(this.tag){
7219             cfg.tag = this.tag;
7220         }
7221         
7222         if (this.html) {
7223             cfg.html=this.html
7224         }
7225         if (this.cls) {
7226             cfg.cls=this.cls
7227         }
7228         if (this.abbr) {
7229             cfg.abbr=this.abbr
7230         }
7231         if (this.align) {
7232             cfg.align=this.align
7233         }
7234         if (this.axis) {
7235             cfg.axis=this.axis
7236         }
7237         if (this.bgcolor) {
7238             cfg.bgcolor=this.bgcolor
7239         }
7240         if (this.charoff) {
7241             cfg.charoff=this.charoff
7242         }
7243         if (this.colspan) {
7244             cfg.colspan=this.colspan
7245         }
7246         if (this.headers) {
7247             cfg.headers=this.headers
7248         }
7249         if (this.height) {
7250             cfg.height=this.height
7251         }
7252         if (this.nowrap) {
7253             cfg.nowrap=this.nowrap
7254         }
7255         if (this.rowspan) {
7256             cfg.rowspan=this.rowspan
7257         }
7258         if (this.scope) {
7259             cfg.scope=this.scope
7260         }
7261         if (this.valign) {
7262             cfg.valign=this.valign
7263         }
7264         if (this.width) {
7265             cfg.width=this.width
7266         }
7267         
7268         
7269         return cfg;
7270     }
7271    
7272 });
7273
7274  
7275
7276  /*
7277  * - LGPL
7278  *
7279  * table row
7280  * 
7281  */
7282
7283 /**
7284  * @class Roo.bootstrap.TableRow
7285  * @extends Roo.bootstrap.Component
7286  * Bootstrap TableRow class
7287  * @cfg {String} cls row class
7288  * @cfg {String} align Aligns the content in a table row
7289  * @cfg {String} bgcolor Specifies a background color for a table row
7290  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7291  * @cfg {String} valign Vertical aligns the content in a table row
7292  * 
7293  * @constructor
7294  * Create a new TableRow
7295  * @param {Object} config The config object
7296  */
7297
7298 Roo.bootstrap.TableRow = function(config){
7299     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7300 };
7301
7302 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7303     
7304     cls: false,
7305     align: false,
7306     bgcolor: false,
7307     charoff: false,
7308     valign: false,
7309     
7310     getAutoCreate : function(){
7311         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7312         
7313         cfg = {
7314             tag: 'tr'
7315         };
7316             
7317         if(this.cls){
7318             cfg.cls = this.cls;
7319         }
7320         if(this.align){
7321             cfg.align = this.align;
7322         }
7323         if(this.bgcolor){
7324             cfg.bgcolor = this.bgcolor;
7325         }
7326         if(this.charoff){
7327             cfg.charoff = this.charoff;
7328         }
7329         if(this.valign){
7330             cfg.valign = this.valign;
7331         }
7332         
7333         return cfg;
7334     }
7335    
7336 });
7337
7338  
7339
7340  /*
7341  * - LGPL
7342  *
7343  * table body
7344  * 
7345  */
7346
7347 /**
7348  * @class Roo.bootstrap.TableBody
7349  * @extends Roo.bootstrap.Component
7350  * Bootstrap TableBody class
7351  * @cfg {String} cls element class
7352  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7353  * @cfg {String} align Aligns the content inside the element
7354  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7355  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7356  * 
7357  * @constructor
7358  * Create a new TableBody
7359  * @param {Object} config The config object
7360  */
7361
7362 Roo.bootstrap.TableBody = function(config){
7363     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7364 };
7365
7366 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7367     
7368     cls: false,
7369     tag: false,
7370     align: false,
7371     charoff: false,
7372     valign: false,
7373     
7374     getAutoCreate : function(){
7375         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7376         
7377         cfg = {
7378             tag: 'tbody'
7379         };
7380             
7381         if (this.cls) {
7382             cfg.cls=this.cls
7383         }
7384         if(this.tag){
7385             cfg.tag = this.tag;
7386         }
7387         
7388         if(this.align){
7389             cfg.align = this.align;
7390         }
7391         if(this.charoff){
7392             cfg.charoff = this.charoff;
7393         }
7394         if(this.valign){
7395             cfg.valign = this.valign;
7396         }
7397         
7398         return cfg;
7399     }
7400     
7401     
7402 //    initEvents : function()
7403 //    {
7404 //        
7405 //        if(!this.store){
7406 //            return;
7407 //        }
7408 //        
7409 //        this.store = Roo.factory(this.store, Roo.data);
7410 //        this.store.on('load', this.onLoad, this);
7411 //        
7412 //        this.store.load();
7413 //        
7414 //    },
7415 //    
7416 //    onLoad: function () 
7417 //    {   
7418 //        this.fireEvent('load', this);
7419 //    }
7420 //    
7421 //   
7422 });
7423
7424  
7425
7426  /*
7427  * Based on:
7428  * Ext JS Library 1.1.1
7429  * Copyright(c) 2006-2007, Ext JS, LLC.
7430  *
7431  * Originally Released Under LGPL - original licence link has changed is not relivant.
7432  *
7433  * Fork - LGPL
7434  * <script type="text/javascript">
7435  */
7436
7437 // as we use this in bootstrap.
7438 Roo.namespace('Roo.form');
7439  /**
7440  * @class Roo.form.Action
7441  * Internal Class used to handle form actions
7442  * @constructor
7443  * @param {Roo.form.BasicForm} el The form element or its id
7444  * @param {Object} config Configuration options
7445  */
7446
7447  
7448  
7449 // define the action interface
7450 Roo.form.Action = function(form, options){
7451     this.form = form;
7452     this.options = options || {};
7453 };
7454 /**
7455  * Client Validation Failed
7456  * @const 
7457  */
7458 Roo.form.Action.CLIENT_INVALID = 'client';
7459 /**
7460  * Server Validation Failed
7461  * @const 
7462  */
7463 Roo.form.Action.SERVER_INVALID = 'server';
7464  /**
7465  * Connect to Server Failed
7466  * @const 
7467  */
7468 Roo.form.Action.CONNECT_FAILURE = 'connect';
7469 /**
7470  * Reading Data from Server Failed
7471  * @const 
7472  */
7473 Roo.form.Action.LOAD_FAILURE = 'load';
7474
7475 Roo.form.Action.prototype = {
7476     type : 'default',
7477     failureType : undefined,
7478     response : undefined,
7479     result : undefined,
7480
7481     // interface method
7482     run : function(options){
7483
7484     },
7485
7486     // interface method
7487     success : function(response){
7488
7489     },
7490
7491     // interface method
7492     handleResponse : function(response){
7493
7494     },
7495
7496     // default connection failure
7497     failure : function(response){
7498         
7499         this.response = response;
7500         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7501         this.form.afterAction(this, false);
7502     },
7503
7504     processResponse : function(response){
7505         this.response = response;
7506         if(!response.responseText){
7507             return true;
7508         }
7509         this.result = this.handleResponse(response);
7510         return this.result;
7511     },
7512
7513     // utility functions used internally
7514     getUrl : function(appendParams){
7515         var url = this.options.url || this.form.url || this.form.el.dom.action;
7516         if(appendParams){
7517             var p = this.getParams();
7518             if(p){
7519                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7520             }
7521         }
7522         return url;
7523     },
7524
7525     getMethod : function(){
7526         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7527     },
7528
7529     getParams : function(){
7530         var bp = this.form.baseParams;
7531         var p = this.options.params;
7532         if(p){
7533             if(typeof p == "object"){
7534                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7535             }else if(typeof p == 'string' && bp){
7536                 p += '&' + Roo.urlEncode(bp);
7537             }
7538         }else if(bp){
7539             p = Roo.urlEncode(bp);
7540         }
7541         return p;
7542     },
7543
7544     createCallback : function(){
7545         return {
7546             success: this.success,
7547             failure: this.failure,
7548             scope: this,
7549             timeout: (this.form.timeout*1000),
7550             upload: this.form.fileUpload ? this.success : undefined
7551         };
7552     }
7553 };
7554
7555 Roo.form.Action.Submit = function(form, options){
7556     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7557 };
7558
7559 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7560     type : 'submit',
7561
7562     haveProgress : false,
7563     uploadComplete : false,
7564     
7565     // uploadProgress indicator.
7566     uploadProgress : function()
7567     {
7568         if (!this.form.progressUrl) {
7569             return;
7570         }
7571         
7572         if (!this.haveProgress) {
7573             Roo.MessageBox.progress("Uploading", "Uploading");
7574         }
7575         if (this.uploadComplete) {
7576            Roo.MessageBox.hide();
7577            return;
7578         }
7579         
7580         this.haveProgress = true;
7581    
7582         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7583         
7584         var c = new Roo.data.Connection();
7585         c.request({
7586             url : this.form.progressUrl,
7587             params: {
7588                 id : uid
7589             },
7590             method: 'GET',
7591             success : function(req){
7592                //console.log(data);
7593                 var rdata = false;
7594                 var edata;
7595                 try  {
7596                    rdata = Roo.decode(req.responseText)
7597                 } catch (e) {
7598                     Roo.log("Invalid data from server..");
7599                     Roo.log(edata);
7600                     return;
7601                 }
7602                 if (!rdata || !rdata.success) {
7603                     Roo.log(rdata);
7604                     Roo.MessageBox.alert(Roo.encode(rdata));
7605                     return;
7606                 }
7607                 var data = rdata.data;
7608                 
7609                 if (this.uploadComplete) {
7610                    Roo.MessageBox.hide();
7611                    return;
7612                 }
7613                    
7614                 if (data){
7615                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7616                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7617                     );
7618                 }
7619                 this.uploadProgress.defer(2000,this);
7620             },
7621        
7622             failure: function(data) {
7623                 Roo.log('progress url failed ');
7624                 Roo.log(data);
7625             },
7626             scope : this
7627         });
7628            
7629     },
7630     
7631     
7632     run : function()
7633     {
7634         // run get Values on the form, so it syncs any secondary forms.
7635         this.form.getValues();
7636         
7637         var o = this.options;
7638         var method = this.getMethod();
7639         var isPost = method == 'POST';
7640         if(o.clientValidation === false || this.form.isValid()){
7641             
7642             if (this.form.progressUrl) {
7643                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7644                     (new Date() * 1) + '' + Math.random());
7645                     
7646             } 
7647             
7648             
7649             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7650                 form:this.form.el.dom,
7651                 url:this.getUrl(!isPost),
7652                 method: method,
7653                 params:isPost ? this.getParams() : null,
7654                 isUpload: this.form.fileUpload
7655             }));
7656             
7657             this.uploadProgress();
7658
7659         }else if (o.clientValidation !== false){ // client validation failed
7660             this.failureType = Roo.form.Action.CLIENT_INVALID;
7661             this.form.afterAction(this, false);
7662         }
7663     },
7664
7665     success : function(response)
7666     {
7667         this.uploadComplete= true;
7668         if (this.haveProgress) {
7669             Roo.MessageBox.hide();
7670         }
7671         
7672         
7673         var result = this.processResponse(response);
7674         if(result === true || result.success){
7675             this.form.afterAction(this, true);
7676             return;
7677         }
7678         if(result.errors){
7679             this.form.markInvalid(result.errors);
7680             this.failureType = Roo.form.Action.SERVER_INVALID;
7681         }
7682         this.form.afterAction(this, false);
7683     },
7684     failure : function(response)
7685     {
7686         this.uploadComplete= true;
7687         if (this.haveProgress) {
7688             Roo.MessageBox.hide();
7689         }
7690         
7691         this.response = response;
7692         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7693         this.form.afterAction(this, false);
7694     },
7695     
7696     handleResponse : function(response){
7697         if(this.form.errorReader){
7698             var rs = this.form.errorReader.read(response);
7699             var errors = [];
7700             if(rs.records){
7701                 for(var i = 0, len = rs.records.length; i < len; i++) {
7702                     var r = rs.records[i];
7703                     errors[i] = r.data;
7704                 }
7705             }
7706             if(errors.length < 1){
7707                 errors = null;
7708             }
7709             return {
7710                 success : rs.success,
7711                 errors : errors
7712             };
7713         }
7714         var ret = false;
7715         try {
7716             ret = Roo.decode(response.responseText);
7717         } catch (e) {
7718             ret = {
7719                 success: false,
7720                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7721                 errors : []
7722             };
7723         }
7724         return ret;
7725         
7726     }
7727 });
7728
7729
7730 Roo.form.Action.Load = function(form, options){
7731     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7732     this.reader = this.form.reader;
7733 };
7734
7735 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7736     type : 'load',
7737
7738     run : function(){
7739         
7740         Roo.Ajax.request(Roo.apply(
7741                 this.createCallback(), {
7742                     method:this.getMethod(),
7743                     url:this.getUrl(false),
7744                     params:this.getParams()
7745         }));
7746     },
7747
7748     success : function(response){
7749         
7750         var result = this.processResponse(response);
7751         if(result === true || !result.success || !result.data){
7752             this.failureType = Roo.form.Action.LOAD_FAILURE;
7753             this.form.afterAction(this, false);
7754             return;
7755         }
7756         this.form.clearInvalid();
7757         this.form.setValues(result.data);
7758         this.form.afterAction(this, true);
7759     },
7760
7761     handleResponse : function(response){
7762         if(this.form.reader){
7763             var rs = this.form.reader.read(response);
7764             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7765             return {
7766                 success : rs.success,
7767                 data : data
7768             };
7769         }
7770         return Roo.decode(response.responseText);
7771     }
7772 });
7773
7774 Roo.form.Action.ACTION_TYPES = {
7775     'load' : Roo.form.Action.Load,
7776     'submit' : Roo.form.Action.Submit
7777 };/*
7778  * - LGPL
7779  *
7780  * form
7781  *
7782  */
7783
7784 /**
7785  * @class Roo.bootstrap.Form
7786  * @extends Roo.bootstrap.Component
7787  * Bootstrap Form class
7788  * @cfg {String} method  GET | POST (default POST)
7789  * @cfg {String} labelAlign top | left (default top)
7790  * @cfg {String} align left  | right - for navbars
7791  * @cfg {Boolean} loadMask load mask when submit (default true)
7792
7793  *
7794  * @constructor
7795  * Create a new Form
7796  * @param {Object} config The config object
7797  */
7798
7799
7800 Roo.bootstrap.Form = function(config){
7801     
7802     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7803     
7804     Roo.bootstrap.Form.popover.apply();
7805     
7806     this.addEvents({
7807         /**
7808          * @event clientvalidation
7809          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7810          * @param {Form} this
7811          * @param {Boolean} valid true if the form has passed client-side validation
7812          */
7813         clientvalidation: true,
7814         /**
7815          * @event beforeaction
7816          * Fires before any action is performed. Return false to cancel the action.
7817          * @param {Form} this
7818          * @param {Action} action The action to be performed
7819          */
7820         beforeaction: true,
7821         /**
7822          * @event actionfailed
7823          * Fires when an action fails.
7824          * @param {Form} this
7825          * @param {Action} action The action that failed
7826          */
7827         actionfailed : true,
7828         /**
7829          * @event actioncomplete
7830          * Fires when an action is completed.
7831          * @param {Form} this
7832          * @param {Action} action The action that completed
7833          */
7834         actioncomplete : true
7835     });
7836 };
7837
7838 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7839
7840      /**
7841      * @cfg {String} method
7842      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7843      */
7844     method : 'POST',
7845     /**
7846      * @cfg {String} url
7847      * The URL to use for form actions if one isn't supplied in the action options.
7848      */
7849     /**
7850      * @cfg {Boolean} fileUpload
7851      * Set to true if this form is a file upload.
7852      */
7853
7854     /**
7855      * @cfg {Object} baseParams
7856      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7857      */
7858
7859     /**
7860      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7861      */
7862     timeout: 30,
7863     /**
7864      * @cfg {Sting} align (left|right) for navbar forms
7865      */
7866     align : 'left',
7867
7868     // private
7869     activeAction : null,
7870
7871     /**
7872      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7873      * element by passing it or its id or mask the form itself by passing in true.
7874      * @type Mixed
7875      */
7876     waitMsgTarget : false,
7877
7878     loadMask : true,
7879     
7880     /**
7881      * @cfg {Boolean} errorMask (true|false) default false
7882      */
7883     errorMask : false,
7884     
7885     /**
7886      * @cfg {Number} maskOffset Default 100
7887      */
7888     maskOffset : 100,
7889     
7890     /**
7891      * @cfg {Boolean} maskBody
7892      */
7893     maskBody : false,
7894
7895     getAutoCreate : function(){
7896
7897         var cfg = {
7898             tag: 'form',
7899             method : this.method || 'POST',
7900             id : this.id || Roo.id(),
7901             cls : ''
7902         };
7903         if (this.parent().xtype.match(/^Nav/)) {
7904             cfg.cls = 'navbar-form navbar-' + this.align;
7905
7906         }
7907
7908         if (this.labelAlign == 'left' ) {
7909             cfg.cls += ' form-horizontal';
7910         }
7911
7912
7913         return cfg;
7914     },
7915     initEvents : function()
7916     {
7917         this.el.on('submit', this.onSubmit, this);
7918         // this was added as random key presses on the form where triggering form submit.
7919         this.el.on('keypress', function(e) {
7920             if (e.getCharCode() != 13) {
7921                 return true;
7922             }
7923             // we might need to allow it for textareas.. and some other items.
7924             // check e.getTarget().
7925
7926             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7927                 return true;
7928             }
7929
7930             Roo.log("keypress blocked");
7931
7932             e.preventDefault();
7933             return false;
7934         });
7935         
7936     },
7937     // private
7938     onSubmit : function(e){
7939         e.stopEvent();
7940     },
7941
7942      /**
7943      * Returns true if client-side validation on the form is successful.
7944      * @return Boolean
7945      */
7946     isValid : function(){
7947         var items = this.getItems();
7948         var valid = true;
7949         var target = false;
7950         
7951         items.each(function(f){
7952             
7953             if(f.validate()){
7954                 return;
7955             }
7956             
7957             Roo.log('invalid field: ' + f.name);
7958             
7959             valid = false;
7960
7961             if(!target && f.el.isVisible(true)){
7962                 target = f;
7963             }
7964            
7965         });
7966         
7967         if(this.errorMask && !valid){
7968             Roo.bootstrap.Form.popover.mask(this, target);
7969         }
7970         
7971         return valid;
7972     },
7973     
7974     /**
7975      * Returns true if any fields in this form have changed since their original load.
7976      * @return Boolean
7977      */
7978     isDirty : function(){
7979         var dirty = false;
7980         var items = this.getItems();
7981         items.each(function(f){
7982            if(f.isDirty()){
7983                dirty = true;
7984                return false;
7985            }
7986            return true;
7987         });
7988         return dirty;
7989     },
7990      /**
7991      * Performs a predefined action (submit or load) or custom actions you define on this form.
7992      * @param {String} actionName The name of the action type
7993      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7994      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7995      * accept other config options):
7996      * <pre>
7997 Property          Type             Description
7998 ----------------  ---------------  ----------------------------------------------------------------------------------
7999 url               String           The url for the action (defaults to the form's url)
8000 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8001 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8002 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8003                                    validate the form on the client (defaults to false)
8004      * </pre>
8005      * @return {BasicForm} this
8006      */
8007     doAction : function(action, options){
8008         if(typeof action == 'string'){
8009             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8010         }
8011         if(this.fireEvent('beforeaction', this, action) !== false){
8012             this.beforeAction(action);
8013             action.run.defer(100, action);
8014         }
8015         return this;
8016     },
8017
8018     // private
8019     beforeAction : function(action){
8020         var o = action.options;
8021         
8022         if(this.loadMask){
8023             
8024             if(this.maskBody){
8025                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8026             } else {
8027                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8028             }
8029         }
8030         // not really supported yet.. ??
8031
8032         //if(this.waitMsgTarget === true){
8033         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8034         //}else if(this.waitMsgTarget){
8035         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8036         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8037         //}else {
8038         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8039        // }
8040
8041     },
8042
8043     // private
8044     afterAction : function(action, success){
8045         this.activeAction = null;
8046         var o = action.options;
8047
8048         if(this.loadMask){
8049             
8050             if(this.maskBody){
8051                 Roo.get(document.body).unmask();
8052             } else {
8053                 this.el.unmask();
8054             }
8055         }
8056         
8057         //if(this.waitMsgTarget === true){
8058 //            this.el.unmask();
8059         //}else if(this.waitMsgTarget){
8060         //    this.waitMsgTarget.unmask();
8061         //}else{
8062         //    Roo.MessageBox.updateProgress(1);
8063         //    Roo.MessageBox.hide();
8064        // }
8065         //
8066         if(success){
8067             if(o.reset){
8068                 this.reset();
8069             }
8070             Roo.callback(o.success, o.scope, [this, action]);
8071             this.fireEvent('actioncomplete', this, action);
8072
8073         }else{
8074
8075             // failure condition..
8076             // we have a scenario where updates need confirming.
8077             // eg. if a locking scenario exists..
8078             // we look for { errors : { needs_confirm : true }} in the response.
8079             if (
8080                 (typeof(action.result) != 'undefined')  &&
8081                 (typeof(action.result.errors) != 'undefined')  &&
8082                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8083            ){
8084                 var _t = this;
8085                 Roo.log("not supported yet");
8086                  /*
8087
8088                 Roo.MessageBox.confirm(
8089                     "Change requires confirmation",
8090                     action.result.errorMsg,
8091                     function(r) {
8092                         if (r != 'yes') {
8093                             return;
8094                         }
8095                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8096                     }
8097
8098                 );
8099                 */
8100
8101
8102                 return;
8103             }
8104
8105             Roo.callback(o.failure, o.scope, [this, action]);
8106             // show an error message if no failed handler is set..
8107             if (!this.hasListener('actionfailed')) {
8108                 Roo.log("need to add dialog support");
8109                 /*
8110                 Roo.MessageBox.alert("Error",
8111                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8112                         action.result.errorMsg :
8113                         "Saving Failed, please check your entries or try again"
8114                 );
8115                 */
8116             }
8117
8118             this.fireEvent('actionfailed', this, action);
8119         }
8120
8121     },
8122     /**
8123      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8124      * @param {String} id The value to search for
8125      * @return Field
8126      */
8127     findField : function(id){
8128         var items = this.getItems();
8129         var field = items.get(id);
8130         if(!field){
8131              items.each(function(f){
8132                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8133                     field = f;
8134                     return false;
8135                 }
8136                 return true;
8137             });
8138         }
8139         return field || null;
8140     },
8141      /**
8142      * Mark fields in this form invalid in bulk.
8143      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8144      * @return {BasicForm} this
8145      */
8146     markInvalid : function(errors){
8147         if(errors instanceof Array){
8148             for(var i = 0, len = errors.length; i < len; i++){
8149                 var fieldError = errors[i];
8150                 var f = this.findField(fieldError.id);
8151                 if(f){
8152                     f.markInvalid(fieldError.msg);
8153                 }
8154             }
8155         }else{
8156             var field, id;
8157             for(id in errors){
8158                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8159                     field.markInvalid(errors[id]);
8160                 }
8161             }
8162         }
8163         //Roo.each(this.childForms || [], function (f) {
8164         //    f.markInvalid(errors);
8165         //});
8166
8167         return this;
8168     },
8169
8170     /**
8171      * Set values for fields in this form in bulk.
8172      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8173      * @return {BasicForm} this
8174      */
8175     setValues : function(values){
8176         if(values instanceof Array){ // array of objects
8177             for(var i = 0, len = values.length; i < len; i++){
8178                 var v = values[i];
8179                 var f = this.findField(v.id);
8180                 if(f){
8181                     f.setValue(v.value);
8182                     if(this.trackResetOnLoad){
8183                         f.originalValue = f.getValue();
8184                     }
8185                 }
8186             }
8187         }else{ // object hash
8188             var field, id;
8189             for(id in values){
8190                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8191
8192                     if (field.setFromData &&
8193                         field.valueField &&
8194                         field.displayField &&
8195                         // combos' with local stores can
8196                         // be queried via setValue()
8197                         // to set their value..
8198                         (field.store && !field.store.isLocal)
8199                         ) {
8200                         // it's a combo
8201                         var sd = { };
8202                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8203                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8204                         field.setFromData(sd);
8205
8206                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8207                         
8208                         field.setFromData(values);
8209                         
8210                     } else {
8211                         field.setValue(values[id]);
8212                     }
8213
8214
8215                     if(this.trackResetOnLoad){
8216                         field.originalValue = field.getValue();
8217                     }
8218                 }
8219             }
8220         }
8221
8222         //Roo.each(this.childForms || [], function (f) {
8223         //    f.setValues(values);
8224         //});
8225
8226         return this;
8227     },
8228
8229     /**
8230      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8231      * they are returned as an array.
8232      * @param {Boolean} asString
8233      * @return {Object}
8234      */
8235     getValues : function(asString){
8236         //if (this.childForms) {
8237             // copy values from the child forms
8238         //    Roo.each(this.childForms, function (f) {
8239         //        this.setValues(f.getValues());
8240         //    }, this);
8241         //}
8242
8243
8244
8245         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8246         if(asString === true){
8247             return fs;
8248         }
8249         return Roo.urlDecode(fs);
8250     },
8251
8252     /**
8253      * Returns the fields in this form as an object with key/value pairs.
8254      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8255      * @return {Object}
8256      */
8257     getFieldValues : function(with_hidden)
8258     {
8259         var items = this.getItems();
8260         var ret = {};
8261         items.each(function(f){
8262             
8263             if (!f.getName()) {
8264                 return;
8265             }
8266             
8267             var v = f.getValue();
8268             
8269             if (f.inputType =='radio') {
8270                 if (typeof(ret[f.getName()]) == 'undefined') {
8271                     ret[f.getName()] = ''; // empty..
8272                 }
8273
8274                 if (!f.el.dom.checked) {
8275                     return;
8276
8277                 }
8278                 v = f.el.dom.value;
8279
8280             }
8281             
8282             if(f.xtype == 'MoneyField'){
8283                 ret[f.currencyName] = f.getCurrency();
8284             }
8285
8286             // not sure if this supported any more..
8287             if ((typeof(v) == 'object') && f.getRawValue) {
8288                 v = f.getRawValue() ; // dates..
8289             }
8290             // combo boxes where name != hiddenName...
8291             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8292                 ret[f.name] = f.getRawValue();
8293             }
8294             ret[f.getName()] = v;
8295         });
8296
8297         return ret;
8298     },
8299
8300     /**
8301      * Clears all invalid messages in this form.
8302      * @return {BasicForm} this
8303      */
8304     clearInvalid : function(){
8305         var items = this.getItems();
8306
8307         items.each(function(f){
8308            f.clearInvalid();
8309         });
8310
8311         return this;
8312     },
8313
8314     /**
8315      * Resets this form.
8316      * @return {BasicForm} this
8317      */
8318     reset : function(){
8319         var items = this.getItems();
8320         items.each(function(f){
8321             f.reset();
8322         });
8323
8324         Roo.each(this.childForms || [], function (f) {
8325             f.reset();
8326         });
8327
8328
8329         return this;
8330     },
8331     
8332     getItems : function()
8333     {
8334         var r=new Roo.util.MixedCollection(false, function(o){
8335             return o.id || (o.id = Roo.id());
8336         });
8337         var iter = function(el) {
8338             if (el.inputEl) {
8339                 r.add(el);
8340             }
8341             if (!el.items) {
8342                 return;
8343             }
8344             Roo.each(el.items,function(e) {
8345                 iter(e);
8346             });
8347         };
8348
8349         iter(this);
8350         return r;
8351     },
8352     
8353     hideFields : function(items)
8354     {
8355         Roo.each(items, function(i){
8356             
8357             var f = this.findField(i);
8358             
8359             if(!f){
8360                 return;
8361             }
8362             
8363             f.hide();
8364             
8365         }, this);
8366     },
8367     
8368     showFields : function(items)
8369     {
8370         Roo.each(items, function(i){
8371             
8372             var f = this.findField(i);
8373             
8374             if(!f){
8375                 return;
8376             }
8377             
8378             f.show();
8379             
8380         }, this);
8381     }
8382
8383 });
8384
8385 Roo.apply(Roo.bootstrap.Form, {
8386     
8387     popover : {
8388         
8389         padding : 5,
8390         
8391         isApplied : false,
8392         
8393         isMasked : false,
8394         
8395         form : false,
8396         
8397         target : false,
8398         
8399         toolTip : false,
8400         
8401         intervalID : false,
8402         
8403         maskEl : false,
8404         
8405         apply : function()
8406         {
8407             if(this.isApplied){
8408                 return;
8409             }
8410             
8411             this.maskEl = {
8412                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8413                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8414                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8415                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8416             };
8417             
8418             this.maskEl.top.enableDisplayMode("block");
8419             this.maskEl.left.enableDisplayMode("block");
8420             this.maskEl.bottom.enableDisplayMode("block");
8421             this.maskEl.right.enableDisplayMode("block");
8422             
8423             this.toolTip = new Roo.bootstrap.Tooltip({
8424                 cls : 'roo-form-error-popover',
8425                 alignment : {
8426                     'left' : ['r-l', [-2,0], 'right'],
8427                     'right' : ['l-r', [2,0], 'left'],
8428                     'bottom' : ['tl-bl', [0,2], 'top'],
8429                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8430                 }
8431             });
8432             
8433             this.toolTip.render(Roo.get(document.body));
8434
8435             this.toolTip.el.enableDisplayMode("block");
8436             
8437             Roo.get(document.body).on('click', function(){
8438                 this.unmask();
8439             }, this);
8440             
8441             Roo.get(document.body).on('touchstart', function(){
8442                 this.unmask();
8443             }, this);
8444             
8445             this.isApplied = true
8446         },
8447         
8448         mask : function(form, target)
8449         {
8450             this.form = form;
8451             
8452             this.target = target;
8453             
8454             if(!this.form.errorMask || !target.el){
8455                 return;
8456             }
8457             
8458             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8459             
8460             Roo.log(scrollable);
8461             
8462             var ot = this.target.el.calcOffsetsTo(scrollable);
8463             
8464             var scrollTo = ot[1] - this.form.maskOffset;
8465             
8466             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8467             
8468             scrollable.scrollTo('top', scrollTo);
8469             
8470             var box = this.target.el.getBox();
8471             Roo.log(box);
8472             var zIndex = Roo.bootstrap.Modal.zIndex++;
8473
8474             
8475             this.maskEl.top.setStyle('position', 'absolute');
8476             this.maskEl.top.setStyle('z-index', zIndex);
8477             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8478             this.maskEl.top.setLeft(0);
8479             this.maskEl.top.setTop(0);
8480             this.maskEl.top.show();
8481             
8482             this.maskEl.left.setStyle('position', 'absolute');
8483             this.maskEl.left.setStyle('z-index', zIndex);
8484             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8485             this.maskEl.left.setLeft(0);
8486             this.maskEl.left.setTop(box.y - this.padding);
8487             this.maskEl.left.show();
8488
8489             this.maskEl.bottom.setStyle('position', 'absolute');
8490             this.maskEl.bottom.setStyle('z-index', zIndex);
8491             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8492             this.maskEl.bottom.setLeft(0);
8493             this.maskEl.bottom.setTop(box.bottom + this.padding);
8494             this.maskEl.bottom.show();
8495
8496             this.maskEl.right.setStyle('position', 'absolute');
8497             this.maskEl.right.setStyle('z-index', zIndex);
8498             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8499             this.maskEl.right.setLeft(box.right + this.padding);
8500             this.maskEl.right.setTop(box.y - this.padding);
8501             this.maskEl.right.show();
8502
8503             this.toolTip.bindEl = this.target.el;
8504
8505             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8506
8507             var tip = this.target.blankText;
8508
8509             if(this.target.getValue() !== '' ) {
8510                 
8511                 if (this.target.invalidText.length) {
8512                     tip = this.target.invalidText;
8513                 } else if (this.target.regexText.length){
8514                     tip = this.target.regexText;
8515                 }
8516             }
8517
8518             this.toolTip.show(tip);
8519
8520             this.intervalID = window.setInterval(function() {
8521                 Roo.bootstrap.Form.popover.unmask();
8522             }, 10000);
8523
8524             window.onwheel = function(){ return false;};
8525             
8526             (function(){ this.isMasked = true; }).defer(500, this);
8527             
8528         },
8529         
8530         unmask : function()
8531         {
8532             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8533                 return;
8534             }
8535             
8536             this.maskEl.top.setStyle('position', 'absolute');
8537             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8538             this.maskEl.top.hide();
8539
8540             this.maskEl.left.setStyle('position', 'absolute');
8541             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8542             this.maskEl.left.hide();
8543
8544             this.maskEl.bottom.setStyle('position', 'absolute');
8545             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8546             this.maskEl.bottom.hide();
8547
8548             this.maskEl.right.setStyle('position', 'absolute');
8549             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8550             this.maskEl.right.hide();
8551             
8552             this.toolTip.hide();
8553             
8554             this.toolTip.el.hide();
8555             
8556             window.onwheel = function(){ return true;};
8557             
8558             if(this.intervalID){
8559                 window.clearInterval(this.intervalID);
8560                 this.intervalID = false;
8561             }
8562             
8563             this.isMasked = false;
8564             
8565         }
8566         
8567     }
8568     
8569 });
8570
8571 /*
8572  * Based on:
8573  * Ext JS Library 1.1.1
8574  * Copyright(c) 2006-2007, Ext JS, LLC.
8575  *
8576  * Originally Released Under LGPL - original licence link has changed is not relivant.
8577  *
8578  * Fork - LGPL
8579  * <script type="text/javascript">
8580  */
8581 /**
8582  * @class Roo.form.VTypes
8583  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8584  * @singleton
8585  */
8586 Roo.form.VTypes = function(){
8587     // closure these in so they are only created once.
8588     var alpha = /^[a-zA-Z_]+$/;
8589     var alphanum = /^[a-zA-Z0-9_]+$/;
8590     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8591     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8592
8593     // All these messages and functions are configurable
8594     return {
8595         /**
8596          * The function used to validate email addresses
8597          * @param {String} value The email address
8598          */
8599         'email' : function(v){
8600             return email.test(v);
8601         },
8602         /**
8603          * The error text to display when the email validation function returns false
8604          * @type String
8605          */
8606         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8607         /**
8608          * The keystroke filter mask to be applied on email input
8609          * @type RegExp
8610          */
8611         'emailMask' : /[a-z0-9_\.\-@]/i,
8612
8613         /**
8614          * The function used to validate URLs
8615          * @param {String} value The URL
8616          */
8617         'url' : function(v){
8618             return url.test(v);
8619         },
8620         /**
8621          * The error text to display when the url validation function returns false
8622          * @type String
8623          */
8624         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8625         
8626         /**
8627          * The function used to validate alpha values
8628          * @param {String} value The value
8629          */
8630         'alpha' : function(v){
8631             return alpha.test(v);
8632         },
8633         /**
8634          * The error text to display when the alpha validation function returns false
8635          * @type String
8636          */
8637         'alphaText' : 'This field should only contain letters and _',
8638         /**
8639          * The keystroke filter mask to be applied on alpha input
8640          * @type RegExp
8641          */
8642         'alphaMask' : /[a-z_]/i,
8643
8644         /**
8645          * The function used to validate alphanumeric values
8646          * @param {String} value The value
8647          */
8648         'alphanum' : function(v){
8649             return alphanum.test(v);
8650         },
8651         /**
8652          * The error text to display when the alphanumeric validation function returns false
8653          * @type String
8654          */
8655         'alphanumText' : 'This field should only contain letters, numbers and _',
8656         /**
8657          * The keystroke filter mask to be applied on alphanumeric input
8658          * @type RegExp
8659          */
8660         'alphanumMask' : /[a-z0-9_]/i
8661     };
8662 }();/*
8663  * - LGPL
8664  *
8665  * Input
8666  * 
8667  */
8668
8669 /**
8670  * @class Roo.bootstrap.Input
8671  * @extends Roo.bootstrap.Component
8672  * Bootstrap Input class
8673  * @cfg {Boolean} disabled is it disabled
8674  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8675  * @cfg {String} name name of the input
8676  * @cfg {string} fieldLabel - the label associated
8677  * @cfg {string} placeholder - placeholder to put in text.
8678  * @cfg {string}  before - input group add on before
8679  * @cfg {string} after - input group add on after
8680  * @cfg {string} size - (lg|sm) or leave empty..
8681  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8682  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8683  * @cfg {Number} md colspan out of 12 for computer-sized screens
8684  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8685  * @cfg {string} value default value of the input
8686  * @cfg {Number} labelWidth set the width of label 
8687  * @cfg {Number} labellg set the width of label (1-12)
8688  * @cfg {Number} labelmd set the width of label (1-12)
8689  * @cfg {Number} labelsm set the width of label (1-12)
8690  * @cfg {Number} labelxs set the width of label (1-12)
8691  * @cfg {String} labelAlign (top|left)
8692  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8693  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8694  * @cfg {String} indicatorpos (left|right) default left
8695  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8696  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8697
8698  * @cfg {String} align (left|center|right) Default left
8699  * @cfg {Boolean} forceFeedback (true|false) Default false
8700  * 
8701  * @constructor
8702  * Create a new Input
8703  * @param {Object} config The config object
8704  */
8705
8706 Roo.bootstrap.Input = function(config){
8707     
8708     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8709     
8710     this.addEvents({
8711         /**
8712          * @event focus
8713          * Fires when this field receives input focus.
8714          * @param {Roo.form.Field} this
8715          */
8716         focus : true,
8717         /**
8718          * @event blur
8719          * Fires when this field loses input focus.
8720          * @param {Roo.form.Field} this
8721          */
8722         blur : true,
8723         /**
8724          * @event specialkey
8725          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8726          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8727          * @param {Roo.form.Field} this
8728          * @param {Roo.EventObject} e The event object
8729          */
8730         specialkey : true,
8731         /**
8732          * @event change
8733          * Fires just before the field blurs if the field value has changed.
8734          * @param {Roo.form.Field} this
8735          * @param {Mixed} newValue The new value
8736          * @param {Mixed} oldValue The original value
8737          */
8738         change : true,
8739         /**
8740          * @event invalid
8741          * Fires after the field has been marked as invalid.
8742          * @param {Roo.form.Field} this
8743          * @param {String} msg The validation message
8744          */
8745         invalid : true,
8746         /**
8747          * @event valid
8748          * Fires after the field has been validated with no errors.
8749          * @param {Roo.form.Field} this
8750          */
8751         valid : true,
8752          /**
8753          * @event keyup
8754          * Fires after the key up
8755          * @param {Roo.form.Field} this
8756          * @param {Roo.EventObject}  e The event Object
8757          */
8758         keyup : true
8759     });
8760 };
8761
8762 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8763      /**
8764      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8765       automatic validation (defaults to "keyup").
8766      */
8767     validationEvent : "keyup",
8768      /**
8769      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8770      */
8771     validateOnBlur : true,
8772     /**
8773      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8774      */
8775     validationDelay : 250,
8776      /**
8777      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8778      */
8779     focusClass : "x-form-focus",  // not needed???
8780     
8781        
8782     /**
8783      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8784      */
8785     invalidClass : "has-warning",
8786     
8787     /**
8788      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8789      */
8790     validClass : "has-success",
8791     
8792     /**
8793      * @cfg {Boolean} hasFeedback (true|false) default true
8794      */
8795     hasFeedback : true,
8796     
8797     /**
8798      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8799      */
8800     invalidFeedbackClass : "glyphicon-warning-sign",
8801     
8802     /**
8803      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8804      */
8805     validFeedbackClass : "glyphicon-ok",
8806     
8807     /**
8808      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8809      */
8810     selectOnFocus : false,
8811     
8812      /**
8813      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8814      */
8815     maskRe : null,
8816        /**
8817      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8818      */
8819     vtype : null,
8820     
8821       /**
8822      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8823      */
8824     disableKeyFilter : false,
8825     
8826        /**
8827      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8828      */
8829     disabled : false,
8830      /**
8831      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8832      */
8833     allowBlank : true,
8834     /**
8835      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8836      */
8837     blankText : "Please complete this mandatory field",
8838     
8839      /**
8840      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8841      */
8842     minLength : 0,
8843     /**
8844      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8845      */
8846     maxLength : Number.MAX_VALUE,
8847     /**
8848      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8849      */
8850     minLengthText : "The minimum length for this field is {0}",
8851     /**
8852      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8853      */
8854     maxLengthText : "The maximum length for this field is {0}",
8855   
8856     
8857     /**
8858      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8859      * If available, this function will be called only after the basic validators all return true, and will be passed the
8860      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8861      */
8862     validator : null,
8863     /**
8864      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8865      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8866      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8867      */
8868     regex : null,
8869     /**
8870      * @cfg {String} regexText -- Depricated - use Invalid Text
8871      */
8872     regexText : "",
8873     
8874     /**
8875      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8876      */
8877     invalidText : "",
8878     
8879     
8880     
8881     autocomplete: false,
8882     
8883     
8884     fieldLabel : '',
8885     inputType : 'text',
8886     
8887     name : false,
8888     placeholder: false,
8889     before : false,
8890     after : false,
8891     size : false,
8892     hasFocus : false,
8893     preventMark: false,
8894     isFormField : true,
8895     value : '',
8896     labelWidth : 2,
8897     labelAlign : false,
8898     readOnly : false,
8899     align : false,
8900     formatedValue : false,
8901     forceFeedback : false,
8902     
8903     indicatorpos : 'left',
8904     
8905     labellg : 0,
8906     labelmd : 0,
8907     labelsm : 0,
8908     labelxs : 0,
8909     
8910     capture : '',
8911     accept : '',
8912     
8913     parentLabelAlign : function()
8914     {
8915         var parent = this;
8916         while (parent.parent()) {
8917             parent = parent.parent();
8918             if (typeof(parent.labelAlign) !='undefined') {
8919                 return parent.labelAlign;
8920             }
8921         }
8922         return 'left';
8923         
8924     },
8925     
8926     getAutoCreate : function()
8927     {
8928         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8929         
8930         var id = Roo.id();
8931         
8932         var cfg = {};
8933         
8934         if(this.inputType != 'hidden'){
8935             cfg.cls = 'form-group' //input-group
8936         }
8937         
8938         var input =  {
8939             tag: 'input',
8940             id : id,
8941             type : this.inputType,
8942             value : this.value,
8943             cls : 'form-control',
8944             placeholder : this.placeholder || '',
8945             autocomplete : this.autocomplete || 'new-password'
8946         };
8947         
8948         if(this.capture.length){
8949             input.capture = this.capture;
8950         }
8951         
8952         if(this.accept.length){
8953             input.accept = this.accept + "/*";
8954         }
8955         
8956         if(this.align){
8957             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8958         }
8959         
8960         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8961             input.maxLength = this.maxLength;
8962         }
8963         
8964         if (this.disabled) {
8965             input.disabled=true;
8966         }
8967         
8968         if (this.readOnly) {
8969             input.readonly=true;
8970         }
8971         
8972         if (this.name) {
8973             input.name = this.name;
8974         }
8975         
8976         if (this.size) {
8977             input.cls += ' input-' + this.size;
8978         }
8979         
8980         var settings=this;
8981         ['xs','sm','md','lg'].map(function(size){
8982             if (settings[size]) {
8983                 cfg.cls += ' col-' + size + '-' + settings[size];
8984             }
8985         });
8986         
8987         var inputblock = input;
8988         
8989         var feedback = {
8990             tag: 'span',
8991             cls: 'glyphicon form-control-feedback'
8992         };
8993             
8994         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8995             
8996             inputblock = {
8997                 cls : 'has-feedback',
8998                 cn :  [
8999                     input,
9000                     feedback
9001                 ] 
9002             };  
9003         }
9004         
9005         if (this.before || this.after) {
9006             
9007             inputblock = {
9008                 cls : 'input-group',
9009                 cn :  [] 
9010             };
9011             
9012             if (this.before && typeof(this.before) == 'string') {
9013                 
9014                 inputblock.cn.push({
9015                     tag :'span',
9016                     cls : 'roo-input-before input-group-addon',
9017                     html : this.before
9018                 });
9019             }
9020             if (this.before && typeof(this.before) == 'object') {
9021                 this.before = Roo.factory(this.before);
9022                 
9023                 inputblock.cn.push({
9024                     tag :'span',
9025                     cls : 'roo-input-before input-group-' +
9026                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9027                 });
9028             }
9029             
9030             inputblock.cn.push(input);
9031             
9032             if (this.after && typeof(this.after) == 'string') {
9033                 inputblock.cn.push({
9034                     tag :'span',
9035                     cls : 'roo-input-after input-group-addon',
9036                     html : this.after
9037                 });
9038             }
9039             if (this.after && typeof(this.after) == 'object') {
9040                 this.after = Roo.factory(this.after);
9041                 
9042                 inputblock.cn.push({
9043                     tag :'span',
9044                     cls : 'roo-input-after input-group-' +
9045                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9046                 });
9047             }
9048             
9049             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9050                 inputblock.cls += ' has-feedback';
9051                 inputblock.cn.push(feedback);
9052             }
9053         };
9054         
9055         if (align ==='left' && this.fieldLabel.length) {
9056             
9057             cfg.cls += ' roo-form-group-label-left';
9058             
9059             cfg.cn = [
9060                 {
9061                     tag : 'i',
9062                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9063                     tooltip : 'This field is required'
9064                 },
9065                 {
9066                     tag: 'label',
9067                     'for' :  id,
9068                     cls : 'control-label',
9069                     html : this.fieldLabel
9070
9071                 },
9072                 {
9073                     cls : "", 
9074                     cn: [
9075                         inputblock
9076                     ]
9077                 }
9078             ];
9079             
9080             var labelCfg = cfg.cn[1];
9081             var contentCfg = cfg.cn[2];
9082             
9083             if(this.indicatorpos == 'right'){
9084                 cfg.cn = [
9085                     {
9086                         tag: 'label',
9087                         'for' :  id,
9088                         cls : 'control-label',
9089                         cn : [
9090                             {
9091                                 tag : 'span',
9092                                 html : this.fieldLabel
9093                             },
9094                             {
9095                                 tag : 'i',
9096                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9097                                 tooltip : 'This field is required'
9098                             }
9099                         ]
9100                     },
9101                     {
9102                         cls : "",
9103                         cn: [
9104                             inputblock
9105                         ]
9106                     }
9107
9108                 ];
9109                 
9110                 labelCfg = cfg.cn[0];
9111                 contentCfg = cfg.cn[1];
9112             
9113             }
9114             
9115             if(this.labelWidth > 12){
9116                 labelCfg.style = "width: " + this.labelWidth + 'px';
9117             }
9118             
9119             if(this.labelWidth < 13 && this.labelmd == 0){
9120                 this.labelmd = this.labelWidth;
9121             }
9122             
9123             if(this.labellg > 0){
9124                 labelCfg.cls += ' col-lg-' + this.labellg;
9125                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9126             }
9127             
9128             if(this.labelmd > 0){
9129                 labelCfg.cls += ' col-md-' + this.labelmd;
9130                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9131             }
9132             
9133             if(this.labelsm > 0){
9134                 labelCfg.cls += ' col-sm-' + this.labelsm;
9135                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9136             }
9137             
9138             if(this.labelxs > 0){
9139                 labelCfg.cls += ' col-xs-' + this.labelxs;
9140                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9141             }
9142             
9143             
9144         } else if ( this.fieldLabel.length) {
9145                 
9146             cfg.cn = [
9147                 {
9148                     tag : 'i',
9149                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9150                     tooltip : 'This field is required'
9151                 },
9152                 {
9153                     tag: 'label',
9154                    //cls : 'input-group-addon',
9155                     html : this.fieldLabel
9156
9157                 },
9158
9159                inputblock
9160
9161            ];
9162            
9163            if(this.indicatorpos == 'right'){
9164                 
9165                 cfg.cn = [
9166                     {
9167                         tag: 'label',
9168                        //cls : 'input-group-addon',
9169                         html : this.fieldLabel
9170
9171                     },
9172                     {
9173                         tag : 'i',
9174                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9175                         tooltip : 'This field is required'
9176                     },
9177
9178                    inputblock
9179
9180                ];
9181
9182             }
9183
9184         } else {
9185             
9186             cfg.cn = [
9187
9188                     inputblock
9189
9190             ];
9191                 
9192                 
9193         };
9194         
9195         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9196            cfg.cls += ' navbar-form';
9197         }
9198         
9199         if (this.parentType === 'NavGroup') {
9200            cfg.cls += ' navbar-form';
9201            cfg.tag = 'li';
9202         }
9203         
9204         return cfg;
9205         
9206     },
9207     /**
9208      * return the real input element.
9209      */
9210     inputEl: function ()
9211     {
9212         return this.el.select('input.form-control',true).first();
9213     },
9214     
9215     tooltipEl : function()
9216     {
9217         return this.inputEl();
9218     },
9219     
9220     indicatorEl : function()
9221     {
9222         var indicator = this.el.select('i.roo-required-indicator',true).first();
9223         
9224         if(!indicator){
9225             return false;
9226         }
9227         
9228         return indicator;
9229         
9230     },
9231     
9232     setDisabled : function(v)
9233     {
9234         var i  = this.inputEl().dom;
9235         if (!v) {
9236             i.removeAttribute('disabled');
9237             return;
9238             
9239         }
9240         i.setAttribute('disabled','true');
9241     },
9242     initEvents : function()
9243     {
9244           
9245         this.inputEl().on("keydown" , this.fireKey,  this);
9246         this.inputEl().on("focus", this.onFocus,  this);
9247         this.inputEl().on("blur", this.onBlur,  this);
9248         
9249         this.inputEl().relayEvent('keyup', this);
9250         
9251         this.indicator = this.indicatorEl();
9252         
9253         if(this.indicator){
9254             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9255         }
9256  
9257         // reference to original value for reset
9258         this.originalValue = this.getValue();
9259         //Roo.form.TextField.superclass.initEvents.call(this);
9260         if(this.validationEvent == 'keyup'){
9261             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9262             this.inputEl().on('keyup', this.filterValidation, this);
9263         }
9264         else if(this.validationEvent !== false){
9265             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9266         }
9267         
9268         if(this.selectOnFocus){
9269             this.on("focus", this.preFocus, this);
9270             
9271         }
9272         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9273             this.inputEl().on("keypress", this.filterKeys, this);
9274         } else {
9275             this.inputEl().relayEvent('keypress', this);
9276         }
9277        /* if(this.grow){
9278             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9279             this.el.on("click", this.autoSize,  this);
9280         }
9281         */
9282         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9283             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9284         }
9285         
9286         if (typeof(this.before) == 'object') {
9287             this.before.render(this.el.select('.roo-input-before',true).first());
9288         }
9289         if (typeof(this.after) == 'object') {
9290             this.after.render(this.el.select('.roo-input-after',true).first());
9291         }
9292         
9293         this.inputEl().on('change', this.onChange, this);
9294         
9295     },
9296     filterValidation : function(e){
9297         if(!e.isNavKeyPress()){
9298             this.validationTask.delay(this.validationDelay);
9299         }
9300     },
9301      /**
9302      * Validates the field value
9303      * @return {Boolean} True if the value is valid, else false
9304      */
9305     validate : function(){
9306         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9307         if(this.disabled || this.validateValue(this.getRawValue())){
9308             this.markValid();
9309             return true;
9310         }
9311         
9312         this.markInvalid();
9313         return false;
9314     },
9315     
9316     
9317     /**
9318      * Validates a value according to the field's validation rules and marks the field as invalid
9319      * if the validation fails
9320      * @param {Mixed} value The value to validate
9321      * @return {Boolean} True if the value is valid, else false
9322      */
9323     validateValue : function(value)
9324     {
9325         if(this.getVisibilityEl().hasClass('hidden')){
9326             return true;
9327         }
9328         
9329         if(value.length < 1)  { // if it's blank
9330             if(this.allowBlank){
9331                 return true;
9332             }
9333             return false;
9334         }
9335         
9336         if(value.length < this.minLength){
9337             return false;
9338         }
9339         if(value.length > this.maxLength){
9340             return false;
9341         }
9342         if(this.vtype){
9343             var vt = Roo.form.VTypes;
9344             if(!vt[this.vtype](value, this)){
9345                 return false;
9346             }
9347         }
9348         if(typeof this.validator == "function"){
9349             var msg = this.validator(value);
9350             if(msg !== true){
9351                 return false;
9352             }
9353             if (typeof(msg) == 'string') {
9354                 this.invalidText = msg;
9355             }
9356         }
9357         
9358         if(this.regex && !this.regex.test(value)){
9359             return false;
9360         }
9361         
9362         return true;
9363     },
9364     
9365      // private
9366     fireKey : function(e){
9367         //Roo.log('field ' + e.getKey());
9368         if(e.isNavKeyPress()){
9369             this.fireEvent("specialkey", this, e);
9370         }
9371     },
9372     focus : function (selectText){
9373         if(this.rendered){
9374             this.inputEl().focus();
9375             if(selectText === true){
9376                 this.inputEl().dom.select();
9377             }
9378         }
9379         return this;
9380     } ,
9381     
9382     onFocus : function(){
9383         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9384            // this.el.addClass(this.focusClass);
9385         }
9386         if(!this.hasFocus){
9387             this.hasFocus = true;
9388             this.startValue = this.getValue();
9389             this.fireEvent("focus", this);
9390         }
9391     },
9392     
9393     beforeBlur : Roo.emptyFn,
9394
9395     
9396     // private
9397     onBlur : function(){
9398         this.beforeBlur();
9399         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9400             //this.el.removeClass(this.focusClass);
9401         }
9402         this.hasFocus = false;
9403         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9404             this.validate();
9405         }
9406         var v = this.getValue();
9407         if(String(v) !== String(this.startValue)){
9408             this.fireEvent('change', this, v, this.startValue);
9409         }
9410         this.fireEvent("blur", this);
9411     },
9412     
9413     onChange : function(e)
9414     {
9415         var v = this.getValue();
9416         if(String(v) !== String(this.startValue)){
9417             this.fireEvent('change', this, v, this.startValue);
9418         }
9419         
9420     },
9421     
9422     /**
9423      * Resets the current field value to the originally loaded value and clears any validation messages
9424      */
9425     reset : function(){
9426         this.setValue(this.originalValue);
9427         this.validate();
9428     },
9429      /**
9430      * Returns the name of the field
9431      * @return {Mixed} name The name field
9432      */
9433     getName: function(){
9434         return this.name;
9435     },
9436      /**
9437      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9438      * @return {Mixed} value The field value
9439      */
9440     getValue : function(){
9441         
9442         var v = this.inputEl().getValue();
9443         
9444         return v;
9445     },
9446     /**
9447      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9448      * @return {Mixed} value The field value
9449      */
9450     getRawValue : function(){
9451         var v = this.inputEl().getValue();
9452         
9453         return v;
9454     },
9455     
9456     /**
9457      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9458      * @param {Mixed} value The value to set
9459      */
9460     setRawValue : function(v){
9461         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9462     },
9463     
9464     selectText : function(start, end){
9465         var v = this.getRawValue();
9466         if(v.length > 0){
9467             start = start === undefined ? 0 : start;
9468             end = end === undefined ? v.length : end;
9469             var d = this.inputEl().dom;
9470             if(d.setSelectionRange){
9471                 d.setSelectionRange(start, end);
9472             }else if(d.createTextRange){
9473                 var range = d.createTextRange();
9474                 range.moveStart("character", start);
9475                 range.moveEnd("character", v.length-end);
9476                 range.select();
9477             }
9478         }
9479     },
9480     
9481     /**
9482      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9483      * @param {Mixed} value The value to set
9484      */
9485     setValue : function(v){
9486         this.value = v;
9487         if(this.rendered){
9488             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9489             this.validate();
9490         }
9491     },
9492     
9493     /*
9494     processValue : function(value){
9495         if(this.stripCharsRe){
9496             var newValue = value.replace(this.stripCharsRe, '');
9497             if(newValue !== value){
9498                 this.setRawValue(newValue);
9499                 return newValue;
9500             }
9501         }
9502         return value;
9503     },
9504   */
9505     preFocus : function(){
9506         
9507         if(this.selectOnFocus){
9508             this.inputEl().dom.select();
9509         }
9510     },
9511     filterKeys : function(e){
9512         var k = e.getKey();
9513         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9514             return;
9515         }
9516         var c = e.getCharCode(), cc = String.fromCharCode(c);
9517         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9518             return;
9519         }
9520         if(!this.maskRe.test(cc)){
9521             e.stopEvent();
9522         }
9523     },
9524      /**
9525      * Clear any invalid styles/messages for this field
9526      */
9527     clearInvalid : function(){
9528         
9529         if(!this.el || this.preventMark){ // not rendered
9530             return;
9531         }
9532         
9533      
9534         this.el.removeClass(this.invalidClass);
9535         
9536         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9537             
9538             var feedback = this.el.select('.form-control-feedback', true).first();
9539             
9540             if(feedback){
9541                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9542             }
9543             
9544         }
9545         
9546         if(this.indicator){
9547             this.indicator.removeClass('visible');
9548             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9549         }
9550         
9551         this.fireEvent('valid', this);
9552     },
9553     
9554      /**
9555      * Mark this field as valid
9556      */
9557     markValid : function()
9558     {
9559         if(!this.el  || this.preventMark){ // not rendered...
9560             return;
9561         }
9562         
9563         this.el.removeClass([this.invalidClass, this.validClass]);
9564         
9565         var feedback = this.el.select('.form-control-feedback', true).first();
9566             
9567         if(feedback){
9568             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9569         }
9570         
9571         if(this.indicator){
9572             this.indicator.removeClass('visible');
9573             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9574         }
9575         
9576         if(this.disabled){
9577             return;
9578         }
9579         
9580         if(this.allowBlank && !this.getRawValue().length){
9581             return;
9582         }
9583         
9584         this.el.addClass(this.validClass);
9585         
9586         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
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                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9593             }
9594             
9595         }
9596         
9597         this.fireEvent('valid', this);
9598     },
9599     
9600      /**
9601      * Mark this field as invalid
9602      * @param {String} msg The validation message
9603      */
9604     markInvalid : function(msg)
9605     {
9606         if(!this.el  || this.preventMark){ // not rendered
9607             return;
9608         }
9609         
9610         this.el.removeClass([this.invalidClass, this.validClass]);
9611         
9612         var feedback = this.el.select('.form-control-feedback', true).first();
9613             
9614         if(feedback){
9615             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9616         }
9617
9618         if(this.disabled){
9619             return;
9620         }
9621         
9622         if(this.allowBlank && !this.getRawValue().length){
9623             return;
9624         }
9625         
9626         if(this.indicator){
9627             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9628             this.indicator.addClass('visible');
9629         }
9630         
9631         this.el.addClass(this.invalidClass);
9632         
9633         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
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                 if(this.getValue().length || this.forceFeedback){
9641                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9642                 }
9643                 
9644             }
9645             
9646         }
9647         
9648         this.fireEvent('invalid', this, msg);
9649     },
9650     // private
9651     SafariOnKeyDown : function(event)
9652     {
9653         // this is a workaround for a password hang bug on chrome/ webkit.
9654         if (this.inputEl().dom.type != 'password') {
9655             return;
9656         }
9657         
9658         var isSelectAll = false;
9659         
9660         if(this.inputEl().dom.selectionEnd > 0){
9661             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9662         }
9663         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9664             event.preventDefault();
9665             this.setValue('');
9666             return;
9667         }
9668         
9669         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9670             
9671             event.preventDefault();
9672             // this is very hacky as keydown always get's upper case.
9673             //
9674             var cc = String.fromCharCode(event.getCharCode());
9675             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9676             
9677         }
9678     },
9679     adjustWidth : function(tag, w){
9680         tag = tag.toLowerCase();
9681         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9682             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9683                 if(tag == 'input'){
9684                     return w + 2;
9685                 }
9686                 if(tag == 'textarea'){
9687                     return w-2;
9688                 }
9689             }else if(Roo.isOpera){
9690                 if(tag == 'input'){
9691                     return w + 2;
9692                 }
9693                 if(tag == 'textarea'){
9694                     return w-2;
9695                 }
9696             }
9697         }
9698         return w;
9699     },
9700     
9701     setFieldLabel : function(v)
9702     {
9703         if(!this.rendered){
9704             return;
9705         }
9706         
9707         if(this.indicator){
9708             var ar = this.el.select('label > span',true);
9709             
9710             if (ar.elements.length) {
9711                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9712                 this.fieldLabel = v;
9713                 return;
9714             }
9715             
9716             var br = this.el.select('label',true);
9717             
9718             if(br.elements.length) {
9719                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9720                 this.fieldLabel = v;
9721                 return;
9722             }
9723             
9724             Roo.log('Cannot Found any of label > span || label in input');
9725             return;
9726         }
9727         
9728         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9729         this.fieldLabel = v;
9730         
9731         
9732     }
9733 });
9734
9735  
9736 /*
9737  * - LGPL
9738  *
9739  * Input
9740  * 
9741  */
9742
9743 /**
9744  * @class Roo.bootstrap.TextArea
9745  * @extends Roo.bootstrap.Input
9746  * Bootstrap TextArea class
9747  * @cfg {Number} cols Specifies the visible width of a text area
9748  * @cfg {Number} rows Specifies the visible number of lines in a text area
9749  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9750  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9751  * @cfg {string} html text
9752  * 
9753  * @constructor
9754  * Create a new TextArea
9755  * @param {Object} config The config object
9756  */
9757
9758 Roo.bootstrap.TextArea = function(config){
9759     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9760    
9761 };
9762
9763 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9764      
9765     cols : false,
9766     rows : 5,
9767     readOnly : false,
9768     warp : 'soft',
9769     resize : false,
9770     value: false,
9771     html: false,
9772     
9773     getAutoCreate : function(){
9774         
9775         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9776         
9777         var id = Roo.id();
9778         
9779         var cfg = {};
9780         
9781         if(this.inputType != 'hidden'){
9782             cfg.cls = 'form-group' //input-group
9783         }
9784         
9785         var input =  {
9786             tag: 'textarea',
9787             id : id,
9788             warp : this.warp,
9789             rows : this.rows,
9790             value : this.value || '',
9791             html: this.html || '',
9792             cls : 'form-control',
9793             placeholder : this.placeholder || '' 
9794             
9795         };
9796         
9797         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9798             input.maxLength = this.maxLength;
9799         }
9800         
9801         if(this.resize){
9802             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9803         }
9804         
9805         if(this.cols){
9806             input.cols = this.cols;
9807         }
9808         
9809         if (this.readOnly) {
9810             input.readonly = true;
9811         }
9812         
9813         if (this.name) {
9814             input.name = this.name;
9815         }
9816         
9817         if (this.size) {
9818             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9819         }
9820         
9821         var settings=this;
9822         ['xs','sm','md','lg'].map(function(size){
9823             if (settings[size]) {
9824                 cfg.cls += ' col-' + size + '-' + settings[size];
9825             }
9826         });
9827         
9828         var inputblock = input;
9829         
9830         if(this.hasFeedback && !this.allowBlank){
9831             
9832             var feedback = {
9833                 tag: 'span',
9834                 cls: 'glyphicon form-control-feedback'
9835             };
9836
9837             inputblock = {
9838                 cls : 'has-feedback',
9839                 cn :  [
9840                     input,
9841                     feedback
9842                 ] 
9843             };  
9844         }
9845         
9846         
9847         if (this.before || this.after) {
9848             
9849             inputblock = {
9850                 cls : 'input-group',
9851                 cn :  [] 
9852             };
9853             if (this.before) {
9854                 inputblock.cn.push({
9855                     tag :'span',
9856                     cls : 'input-group-addon',
9857                     html : this.before
9858                 });
9859             }
9860             
9861             inputblock.cn.push(input);
9862             
9863             if(this.hasFeedback && !this.allowBlank){
9864                 inputblock.cls += ' has-feedback';
9865                 inputblock.cn.push(feedback);
9866             }
9867             
9868             if (this.after) {
9869                 inputblock.cn.push({
9870                     tag :'span',
9871                     cls : 'input-group-addon',
9872                     html : this.after
9873                 });
9874             }
9875             
9876         }
9877         
9878         if (align ==='left' && this.fieldLabel.length) {
9879             cfg.cn = [
9880                 {
9881                     tag: 'label',
9882                     'for' :  id,
9883                     cls : 'control-label',
9884                     html : this.fieldLabel
9885                 },
9886                 {
9887                     cls : "",
9888                     cn: [
9889                         inputblock
9890                     ]
9891                 }
9892
9893             ];
9894             
9895             if(this.labelWidth > 12){
9896                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9897             }
9898
9899             if(this.labelWidth < 13 && this.labelmd == 0){
9900                 this.labelmd = this.labelWidth;
9901             }
9902
9903             if(this.labellg > 0){
9904                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9905                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9906             }
9907
9908             if(this.labelmd > 0){
9909                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9910                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9911             }
9912
9913             if(this.labelsm > 0){
9914                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9915                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9916             }
9917
9918             if(this.labelxs > 0){
9919                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9920                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9921             }
9922             
9923         } else if ( this.fieldLabel.length) {
9924             cfg.cn = [
9925
9926                {
9927                    tag: 'label',
9928                    //cls : 'input-group-addon',
9929                    html : this.fieldLabel
9930
9931                },
9932
9933                inputblock
9934
9935            ];
9936
9937         } else {
9938
9939             cfg.cn = [
9940
9941                 inputblock
9942
9943             ];
9944                 
9945         }
9946         
9947         if (this.disabled) {
9948             input.disabled=true;
9949         }
9950         
9951         return cfg;
9952         
9953     },
9954     /**
9955      * return the real textarea element.
9956      */
9957     inputEl: function ()
9958     {
9959         return this.el.select('textarea.form-control',true).first();
9960     },
9961     
9962     /**
9963      * Clear any invalid styles/messages for this field
9964      */
9965     clearInvalid : function()
9966     {
9967         
9968         if(!this.el || this.preventMark){ // not rendered
9969             return;
9970         }
9971         
9972         var label = this.el.select('label', true).first();
9973         var icon = this.el.select('i.fa-star', true).first();
9974         
9975         if(label && icon){
9976             icon.remove();
9977         }
9978         
9979         this.el.removeClass(this.invalidClass);
9980         
9981         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9982             
9983             var feedback = this.el.select('.form-control-feedback', true).first();
9984             
9985             if(feedback){
9986                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9987             }
9988             
9989         }
9990         
9991         this.fireEvent('valid', this);
9992     },
9993     
9994      /**
9995      * Mark this field as valid
9996      */
9997     markValid : function()
9998     {
9999         if(!this.el  || this.preventMark){ // not rendered
10000             return;
10001         }
10002         
10003         this.el.removeClass([this.invalidClass, this.validClass]);
10004         
10005         var feedback = this.el.select('.form-control-feedback', true).first();
10006             
10007         if(feedback){
10008             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10009         }
10010
10011         if(this.disabled || this.allowBlank){
10012             return;
10013         }
10014         
10015         var label = this.el.select('label', true).first();
10016         var icon = this.el.select('i.fa-star', true).first();
10017         
10018         if(label && icon){
10019             icon.remove();
10020         }
10021         
10022         this.el.addClass(this.validClass);
10023         
10024         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10025             
10026             var feedback = this.el.select('.form-control-feedback', true).first();
10027             
10028             if(feedback){
10029                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10030                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10031             }
10032             
10033         }
10034         
10035         this.fireEvent('valid', this);
10036     },
10037     
10038      /**
10039      * Mark this field as invalid
10040      * @param {String} msg The validation message
10041      */
10042     markInvalid : function(msg)
10043     {
10044         if(!this.el  || this.preventMark){ // not rendered
10045             return;
10046         }
10047         
10048         this.el.removeClass([this.invalidClass, this.validClass]);
10049         
10050         var feedback = this.el.select('.form-control-feedback', true).first();
10051             
10052         if(feedback){
10053             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10054         }
10055
10056         if(this.disabled || this.allowBlank){
10057             return;
10058         }
10059         
10060         var label = this.el.select('label', true).first();
10061         var icon = this.el.select('i.fa-star', true).first();
10062         
10063         if(!this.getValue().length && label && !icon){
10064             this.el.createChild({
10065                 tag : 'i',
10066                 cls : 'text-danger fa fa-lg fa-star',
10067                 tooltip : 'This field is required',
10068                 style : 'margin-right:5px;'
10069             }, label, true);
10070         }
10071
10072         this.el.addClass(this.invalidClass);
10073         
10074         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10075             
10076             var feedback = this.el.select('.form-control-feedback', true).first();
10077             
10078             if(feedback){
10079                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10080                 
10081                 if(this.getValue().length || this.forceFeedback){
10082                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10083                 }
10084                 
10085             }
10086             
10087         }
10088         
10089         this.fireEvent('invalid', this, msg);
10090     }
10091 });
10092
10093  
10094 /*
10095  * - LGPL
10096  *
10097  * trigger field - base class for combo..
10098  * 
10099  */
10100  
10101 /**
10102  * @class Roo.bootstrap.TriggerField
10103  * @extends Roo.bootstrap.Input
10104  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10105  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10106  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10107  * for which you can provide a custom implementation.  For example:
10108  * <pre><code>
10109 var trigger = new Roo.bootstrap.TriggerField();
10110 trigger.onTriggerClick = myTriggerFn;
10111 trigger.applyTo('my-field');
10112 </code></pre>
10113  *
10114  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10115  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10116  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10117  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10118  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10119
10120  * @constructor
10121  * Create a new TriggerField.
10122  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10123  * to the base TextField)
10124  */
10125 Roo.bootstrap.TriggerField = function(config){
10126     this.mimicing = false;
10127     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10128 };
10129
10130 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10131     /**
10132      * @cfg {String} triggerClass A CSS class to apply to the trigger
10133      */
10134      /**
10135      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10136      */
10137     hideTrigger:false,
10138
10139     /**
10140      * @cfg {Boolean} removable (true|false) special filter default false
10141      */
10142     removable : false,
10143     
10144     /** @cfg {Boolean} grow @hide */
10145     /** @cfg {Number} growMin @hide */
10146     /** @cfg {Number} growMax @hide */
10147
10148     /**
10149      * @hide 
10150      * @method
10151      */
10152     autoSize: Roo.emptyFn,
10153     // private
10154     monitorTab : true,
10155     // private
10156     deferHeight : true,
10157
10158     
10159     actionMode : 'wrap',
10160     
10161     caret : false,
10162     
10163     
10164     getAutoCreate : function(){
10165        
10166         var align = this.labelAlign || this.parentLabelAlign();
10167         
10168         var id = Roo.id();
10169         
10170         var cfg = {
10171             cls: 'form-group' //input-group
10172         };
10173         
10174         
10175         var input =  {
10176             tag: 'input',
10177             id : id,
10178             type : this.inputType,
10179             cls : 'form-control',
10180             autocomplete: 'new-password',
10181             placeholder : this.placeholder || '' 
10182             
10183         };
10184         if (this.name) {
10185             input.name = this.name;
10186         }
10187         if (this.size) {
10188             input.cls += ' input-' + this.size;
10189         }
10190         
10191         if (this.disabled) {
10192             input.disabled=true;
10193         }
10194         
10195         var inputblock = input;
10196         
10197         if(this.hasFeedback && !this.allowBlank){
10198             
10199             var feedback = {
10200                 tag: 'span',
10201                 cls: 'glyphicon form-control-feedback'
10202             };
10203             
10204             if(this.removable && !this.editable && !this.tickable){
10205                 inputblock = {
10206                     cls : 'has-feedback',
10207                     cn :  [
10208                         inputblock,
10209                         {
10210                             tag: 'button',
10211                             html : 'x',
10212                             cls : 'roo-combo-removable-btn close'
10213                         },
10214                         feedback
10215                     ] 
10216                 };
10217             } else {
10218                 inputblock = {
10219                     cls : 'has-feedback',
10220                     cn :  [
10221                         inputblock,
10222                         feedback
10223                     ] 
10224                 };
10225             }
10226
10227         } else {
10228             if(this.removable && !this.editable && !this.tickable){
10229                 inputblock = {
10230                     cls : 'roo-removable',
10231                     cn :  [
10232                         inputblock,
10233                         {
10234                             tag: 'button',
10235                             html : 'x',
10236                             cls : 'roo-combo-removable-btn close'
10237                         }
10238                     ] 
10239                 };
10240             }
10241         }
10242         
10243         if (this.before || this.after) {
10244             
10245             inputblock = {
10246                 cls : 'input-group',
10247                 cn :  [] 
10248             };
10249             if (this.before) {
10250                 inputblock.cn.push({
10251                     tag :'span',
10252                     cls : 'input-group-addon',
10253                     html : this.before
10254                 });
10255             }
10256             
10257             inputblock.cn.push(input);
10258             
10259             if(this.hasFeedback && !this.allowBlank){
10260                 inputblock.cls += ' has-feedback';
10261                 inputblock.cn.push(feedback);
10262             }
10263             
10264             if (this.after) {
10265                 inputblock.cn.push({
10266                     tag :'span',
10267                     cls : 'input-group-addon',
10268                     html : this.after
10269                 });
10270             }
10271             
10272         };
10273         
10274         var box = {
10275             tag: 'div',
10276             cn: [
10277                 {
10278                     tag: 'input',
10279                     type : 'hidden',
10280                     cls: 'form-hidden-field'
10281                 },
10282                 inputblock
10283             ]
10284             
10285         };
10286         
10287         if(this.multiple){
10288             box = {
10289                 tag: 'div',
10290                 cn: [
10291                     {
10292                         tag: 'input',
10293                         type : 'hidden',
10294                         cls: 'form-hidden-field'
10295                     },
10296                     {
10297                         tag: 'ul',
10298                         cls: 'roo-select2-choices',
10299                         cn:[
10300                             {
10301                                 tag: 'li',
10302                                 cls: 'roo-select2-search-field',
10303                                 cn: [
10304
10305                                     inputblock
10306                                 ]
10307                             }
10308                         ]
10309                     }
10310                 ]
10311             }
10312         };
10313         
10314         var combobox = {
10315             cls: 'roo-select2-container input-group',
10316             cn: [
10317                 box
10318 //                {
10319 //                    tag: 'ul',
10320 //                    cls: 'typeahead typeahead-long dropdown-menu',
10321 //                    style: 'display:none'
10322 //                }
10323             ]
10324         };
10325         
10326         if(!this.multiple && this.showToggleBtn){
10327             
10328             var caret = {
10329                         tag: 'span',
10330                         cls: 'caret'
10331              };
10332             if (this.caret != false) {
10333                 caret = {
10334                      tag: 'i',
10335                      cls: 'fa fa-' + this.caret
10336                 };
10337                 
10338             }
10339             
10340             combobox.cn.push({
10341                 tag :'span',
10342                 cls : 'input-group-addon btn dropdown-toggle',
10343                 cn : [
10344                     caret,
10345                     {
10346                         tag: 'span',
10347                         cls: 'combobox-clear',
10348                         cn  : [
10349                             {
10350                                 tag : 'i',
10351                                 cls: 'icon-remove'
10352                             }
10353                         ]
10354                     }
10355                 ]
10356
10357             })
10358         }
10359         
10360         if(this.multiple){
10361             combobox.cls += ' roo-select2-container-multi';
10362         }
10363         
10364         if (align ==='left' && this.fieldLabel.length) {
10365             
10366             cfg.cls += ' roo-form-group-label-left';
10367
10368             cfg.cn = [
10369                 {
10370                     tag : 'i',
10371                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10372                     tooltip : 'This field is required'
10373                 },
10374                 {
10375                     tag: 'label',
10376                     'for' :  id,
10377                     cls : 'control-label',
10378                     html : this.fieldLabel
10379
10380                 },
10381                 {
10382                     cls : "", 
10383                     cn: [
10384                         combobox
10385                     ]
10386                 }
10387
10388             ];
10389             
10390             var labelCfg = cfg.cn[1];
10391             var contentCfg = cfg.cn[2];
10392             
10393             if(this.indicatorpos == 'right'){
10394                 cfg.cn = [
10395                     {
10396                         tag: 'label',
10397                         'for' :  id,
10398                         cls : 'control-label',
10399                         cn : [
10400                             {
10401                                 tag : 'span',
10402                                 html : this.fieldLabel
10403                             },
10404                             {
10405                                 tag : 'i',
10406                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10407                                 tooltip : 'This field is required'
10408                             }
10409                         ]
10410                     },
10411                     {
10412                         cls : "", 
10413                         cn: [
10414                             combobox
10415                         ]
10416                     }
10417
10418                 ];
10419                 
10420                 labelCfg = cfg.cn[0];
10421                 contentCfg = cfg.cn[1];
10422             }
10423             
10424             if(this.labelWidth > 12){
10425                 labelCfg.style = "width: " + this.labelWidth + 'px';
10426             }
10427             
10428             if(this.labelWidth < 13 && this.labelmd == 0){
10429                 this.labelmd = this.labelWidth;
10430             }
10431             
10432             if(this.labellg > 0){
10433                 labelCfg.cls += ' col-lg-' + this.labellg;
10434                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10435             }
10436             
10437             if(this.labelmd > 0){
10438                 labelCfg.cls += ' col-md-' + this.labelmd;
10439                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10440             }
10441             
10442             if(this.labelsm > 0){
10443                 labelCfg.cls += ' col-sm-' + this.labelsm;
10444                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10445             }
10446             
10447             if(this.labelxs > 0){
10448                 labelCfg.cls += ' col-xs-' + this.labelxs;
10449                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10450             }
10451             
10452         } else if ( this.fieldLabel.length) {
10453 //                Roo.log(" label");
10454             cfg.cn = [
10455                 {
10456                    tag : 'i',
10457                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10458                    tooltip : 'This field is required'
10459                },
10460                {
10461                    tag: 'label',
10462                    //cls : 'input-group-addon',
10463                    html : this.fieldLabel
10464
10465                },
10466
10467                combobox
10468
10469             ];
10470             
10471             if(this.indicatorpos == 'right'){
10472                 
10473                 cfg.cn = [
10474                     {
10475                        tag: 'label',
10476                        cn : [
10477                            {
10478                                tag : 'span',
10479                                html : this.fieldLabel
10480                            },
10481                            {
10482                               tag : 'i',
10483                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10484                               tooltip : 'This field is required'
10485                            }
10486                        ]
10487
10488                     },
10489                     combobox
10490
10491                 ];
10492
10493             }
10494
10495         } else {
10496             
10497 //                Roo.log(" no label && no align");
10498                 cfg = combobox
10499                      
10500                 
10501         }
10502         
10503         var settings=this;
10504         ['xs','sm','md','lg'].map(function(size){
10505             if (settings[size]) {
10506                 cfg.cls += ' col-' + size + '-' + settings[size];
10507             }
10508         });
10509         
10510         return cfg;
10511         
10512     },
10513     
10514     
10515     
10516     // private
10517     onResize : function(w, h){
10518 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10519 //        if(typeof w == 'number'){
10520 //            var x = w - this.trigger.getWidth();
10521 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10522 //            this.trigger.setStyle('left', x+'px');
10523 //        }
10524     },
10525
10526     // private
10527     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10528
10529     // private
10530     getResizeEl : function(){
10531         return this.inputEl();
10532     },
10533
10534     // private
10535     getPositionEl : function(){
10536         return this.inputEl();
10537     },
10538
10539     // private
10540     alignErrorIcon : function(){
10541         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10542     },
10543
10544     // private
10545     initEvents : function(){
10546         
10547         this.createList();
10548         
10549         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10550         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10551         if(!this.multiple && this.showToggleBtn){
10552             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10553             if(this.hideTrigger){
10554                 this.trigger.setDisplayed(false);
10555             }
10556             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10557         }
10558         
10559         if(this.multiple){
10560             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10561         }
10562         
10563         if(this.removable && !this.editable && !this.tickable){
10564             var close = this.closeTriggerEl();
10565             
10566             if(close){
10567                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10568                 close.on('click', this.removeBtnClick, this, close);
10569             }
10570         }
10571         
10572         //this.trigger.addClassOnOver('x-form-trigger-over');
10573         //this.trigger.addClassOnClick('x-form-trigger-click');
10574         
10575         //if(!this.width){
10576         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10577         //}
10578     },
10579     
10580     closeTriggerEl : function()
10581     {
10582         var close = this.el.select('.roo-combo-removable-btn', true).first();
10583         return close ? close : false;
10584     },
10585     
10586     removeBtnClick : function(e, h, el)
10587     {
10588         e.preventDefault();
10589         
10590         if(this.fireEvent("remove", this) !== false){
10591             this.reset();
10592             this.fireEvent("afterremove", this)
10593         }
10594     },
10595     
10596     createList : function()
10597     {
10598         this.list = Roo.get(document.body).createChild({
10599             tag: 'ul',
10600             cls: 'typeahead typeahead-long dropdown-menu',
10601             style: 'display:none'
10602         });
10603         
10604         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10605         
10606     },
10607
10608     // private
10609     initTrigger : function(){
10610        
10611     },
10612
10613     // private
10614     onDestroy : function(){
10615         if(this.trigger){
10616             this.trigger.removeAllListeners();
10617           //  this.trigger.remove();
10618         }
10619         //if(this.wrap){
10620         //    this.wrap.remove();
10621         //}
10622         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10623     },
10624
10625     // private
10626     onFocus : function(){
10627         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10628         /*
10629         if(!this.mimicing){
10630             this.wrap.addClass('x-trigger-wrap-focus');
10631             this.mimicing = true;
10632             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10633             if(this.monitorTab){
10634                 this.el.on("keydown", this.checkTab, this);
10635             }
10636         }
10637         */
10638     },
10639
10640     // private
10641     checkTab : function(e){
10642         if(e.getKey() == e.TAB){
10643             this.triggerBlur();
10644         }
10645     },
10646
10647     // private
10648     onBlur : function(){
10649         // do nothing
10650     },
10651
10652     // private
10653     mimicBlur : function(e, t){
10654         /*
10655         if(!this.wrap.contains(t) && this.validateBlur()){
10656             this.triggerBlur();
10657         }
10658         */
10659     },
10660
10661     // private
10662     triggerBlur : function(){
10663         this.mimicing = false;
10664         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10665         if(this.monitorTab){
10666             this.el.un("keydown", this.checkTab, this);
10667         }
10668         //this.wrap.removeClass('x-trigger-wrap-focus');
10669         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10670     },
10671
10672     // private
10673     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10674     validateBlur : function(e, t){
10675         return true;
10676     },
10677
10678     // private
10679     onDisable : function(){
10680         this.inputEl().dom.disabled = true;
10681         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10682         //if(this.wrap){
10683         //    this.wrap.addClass('x-item-disabled');
10684         //}
10685     },
10686
10687     // private
10688     onEnable : function(){
10689         this.inputEl().dom.disabled = false;
10690         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10691         //if(this.wrap){
10692         //    this.el.removeClass('x-item-disabled');
10693         //}
10694     },
10695
10696     // private
10697     onShow : function(){
10698         var ae = this.getActionEl();
10699         
10700         if(ae){
10701             ae.dom.style.display = '';
10702             ae.dom.style.visibility = 'visible';
10703         }
10704     },
10705
10706     // private
10707     
10708     onHide : function(){
10709         var ae = this.getActionEl();
10710         ae.dom.style.display = 'none';
10711     },
10712
10713     /**
10714      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10715      * by an implementing function.
10716      * @method
10717      * @param {EventObject} e
10718      */
10719     onTriggerClick : Roo.emptyFn
10720 });
10721  /*
10722  * Based on:
10723  * Ext JS Library 1.1.1
10724  * Copyright(c) 2006-2007, Ext JS, LLC.
10725  *
10726  * Originally Released Under LGPL - original licence link has changed is not relivant.
10727  *
10728  * Fork - LGPL
10729  * <script type="text/javascript">
10730  */
10731
10732
10733 /**
10734  * @class Roo.data.SortTypes
10735  * @singleton
10736  * Defines the default sorting (casting?) comparison functions used when sorting data.
10737  */
10738 Roo.data.SortTypes = {
10739     /**
10740      * Default sort that does nothing
10741      * @param {Mixed} s The value being converted
10742      * @return {Mixed} The comparison value
10743      */
10744     none : function(s){
10745         return s;
10746     },
10747     
10748     /**
10749      * The regular expression used to strip tags
10750      * @type {RegExp}
10751      * @property
10752      */
10753     stripTagsRE : /<\/?[^>]+>/gi,
10754     
10755     /**
10756      * Strips all HTML tags to sort on text only
10757      * @param {Mixed} s The value being converted
10758      * @return {String} The comparison value
10759      */
10760     asText : function(s){
10761         return String(s).replace(this.stripTagsRE, "");
10762     },
10763     
10764     /**
10765      * Strips all HTML tags to sort on text only - Case insensitive
10766      * @param {Mixed} s The value being converted
10767      * @return {String} The comparison value
10768      */
10769     asUCText : function(s){
10770         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10771     },
10772     
10773     /**
10774      * Case insensitive string
10775      * @param {Mixed} s The value being converted
10776      * @return {String} The comparison value
10777      */
10778     asUCString : function(s) {
10779         return String(s).toUpperCase();
10780     },
10781     
10782     /**
10783      * Date sorting
10784      * @param {Mixed} s The value being converted
10785      * @return {Number} The comparison value
10786      */
10787     asDate : function(s) {
10788         if(!s){
10789             return 0;
10790         }
10791         if(s instanceof Date){
10792             return s.getTime();
10793         }
10794         return Date.parse(String(s));
10795     },
10796     
10797     /**
10798      * Float sorting
10799      * @param {Mixed} s The value being converted
10800      * @return {Float} The comparison value
10801      */
10802     asFloat : function(s) {
10803         var val = parseFloat(String(s).replace(/,/g, ""));
10804         if(isNaN(val)) {
10805             val = 0;
10806         }
10807         return val;
10808     },
10809     
10810     /**
10811      * Integer sorting
10812      * @param {Mixed} s The value being converted
10813      * @return {Number} The comparison value
10814      */
10815     asInt : function(s) {
10816         var val = parseInt(String(s).replace(/,/g, ""));
10817         if(isNaN(val)) {
10818             val = 0;
10819         }
10820         return val;
10821     }
10822 };/*
10823  * Based on:
10824  * Ext JS Library 1.1.1
10825  * Copyright(c) 2006-2007, Ext JS, LLC.
10826  *
10827  * Originally Released Under LGPL - original licence link has changed is not relivant.
10828  *
10829  * Fork - LGPL
10830  * <script type="text/javascript">
10831  */
10832
10833 /**
10834 * @class Roo.data.Record
10835  * Instances of this class encapsulate both record <em>definition</em> information, and record
10836  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10837  * to access Records cached in an {@link Roo.data.Store} object.<br>
10838  * <p>
10839  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10840  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10841  * objects.<br>
10842  * <p>
10843  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10844  * @constructor
10845  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10846  * {@link #create}. The parameters are the same.
10847  * @param {Array} data An associative Array of data values keyed by the field name.
10848  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10849  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10850  * not specified an integer id is generated.
10851  */
10852 Roo.data.Record = function(data, id){
10853     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10854     this.data = data;
10855 };
10856
10857 /**
10858  * Generate a constructor for a specific record layout.
10859  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10860  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10861  * Each field definition object may contain the following properties: <ul>
10862  * <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,
10863  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10864  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10865  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10866  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10867  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10868  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10869  * this may be omitted.</p></li>
10870  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10871  * <ul><li>auto (Default, implies no conversion)</li>
10872  * <li>string</li>
10873  * <li>int</li>
10874  * <li>float</li>
10875  * <li>boolean</li>
10876  * <li>date</li></ul></p></li>
10877  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10878  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10879  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10880  * by the Reader into an object that will be stored in the Record. It is passed the
10881  * following parameters:<ul>
10882  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10883  * </ul></p></li>
10884  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10885  * </ul>
10886  * <br>usage:<br><pre><code>
10887 var TopicRecord = Roo.data.Record.create(
10888     {name: 'title', mapping: 'topic_title'},
10889     {name: 'author', mapping: 'username'},
10890     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10891     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10892     {name: 'lastPoster', mapping: 'user2'},
10893     {name: 'excerpt', mapping: 'post_text'}
10894 );
10895
10896 var myNewRecord = new TopicRecord({
10897     title: 'Do my job please',
10898     author: 'noobie',
10899     totalPosts: 1,
10900     lastPost: new Date(),
10901     lastPoster: 'Animal',
10902     excerpt: 'No way dude!'
10903 });
10904 myStore.add(myNewRecord);
10905 </code></pre>
10906  * @method create
10907  * @static
10908  */
10909 Roo.data.Record.create = function(o){
10910     var f = function(){
10911         f.superclass.constructor.apply(this, arguments);
10912     };
10913     Roo.extend(f, Roo.data.Record);
10914     var p = f.prototype;
10915     p.fields = new Roo.util.MixedCollection(false, function(field){
10916         return field.name;
10917     });
10918     for(var i = 0, len = o.length; i < len; i++){
10919         p.fields.add(new Roo.data.Field(o[i]));
10920     }
10921     f.getField = function(name){
10922         return p.fields.get(name);  
10923     };
10924     return f;
10925 };
10926
10927 Roo.data.Record.AUTO_ID = 1000;
10928 Roo.data.Record.EDIT = 'edit';
10929 Roo.data.Record.REJECT = 'reject';
10930 Roo.data.Record.COMMIT = 'commit';
10931
10932 Roo.data.Record.prototype = {
10933     /**
10934      * Readonly flag - true if this record has been modified.
10935      * @type Boolean
10936      */
10937     dirty : false,
10938     editing : false,
10939     error: null,
10940     modified: null,
10941
10942     // private
10943     join : function(store){
10944         this.store = store;
10945     },
10946
10947     /**
10948      * Set the named field to the specified value.
10949      * @param {String} name The name of the field to set.
10950      * @param {Object} value The value to set the field to.
10951      */
10952     set : function(name, value){
10953         if(this.data[name] == value){
10954             return;
10955         }
10956         this.dirty = true;
10957         if(!this.modified){
10958             this.modified = {};
10959         }
10960         if(typeof this.modified[name] == 'undefined'){
10961             this.modified[name] = this.data[name];
10962         }
10963         this.data[name] = value;
10964         if(!this.editing && this.store){
10965             this.store.afterEdit(this);
10966         }       
10967     },
10968
10969     /**
10970      * Get the value of the named field.
10971      * @param {String} name The name of the field to get the value of.
10972      * @return {Object} The value of the field.
10973      */
10974     get : function(name){
10975         return this.data[name]; 
10976     },
10977
10978     // private
10979     beginEdit : function(){
10980         this.editing = true;
10981         this.modified = {}; 
10982     },
10983
10984     // private
10985     cancelEdit : function(){
10986         this.editing = false;
10987         delete this.modified;
10988     },
10989
10990     // private
10991     endEdit : function(){
10992         this.editing = false;
10993         if(this.dirty && this.store){
10994             this.store.afterEdit(this);
10995         }
10996     },
10997
10998     /**
10999      * Usually called by the {@link Roo.data.Store} which owns the Record.
11000      * Rejects all changes made to the Record since either creation, or the last commit operation.
11001      * Modified fields are reverted to their original values.
11002      * <p>
11003      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11004      * of reject operations.
11005      */
11006     reject : function(){
11007         var m = this.modified;
11008         for(var n in m){
11009             if(typeof m[n] != "function"){
11010                 this.data[n] = m[n];
11011             }
11012         }
11013         this.dirty = false;
11014         delete this.modified;
11015         this.editing = false;
11016         if(this.store){
11017             this.store.afterReject(this);
11018         }
11019     },
11020
11021     /**
11022      * Usually called by the {@link Roo.data.Store} which owns the Record.
11023      * Commits all changes made to the Record since either creation, or the last commit operation.
11024      * <p>
11025      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11026      * of commit operations.
11027      */
11028     commit : function(){
11029         this.dirty = false;
11030         delete this.modified;
11031         this.editing = false;
11032         if(this.store){
11033             this.store.afterCommit(this);
11034         }
11035     },
11036
11037     // private
11038     hasError : function(){
11039         return this.error != null;
11040     },
11041
11042     // private
11043     clearError : function(){
11044         this.error = null;
11045     },
11046
11047     /**
11048      * Creates a copy of this record.
11049      * @param {String} id (optional) A new record id if you don't want to use this record's id
11050      * @return {Record}
11051      */
11052     copy : function(newId) {
11053         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11054     }
11055 };/*
11056  * Based on:
11057  * Ext JS Library 1.1.1
11058  * Copyright(c) 2006-2007, Ext JS, LLC.
11059  *
11060  * Originally Released Under LGPL - original licence link has changed is not relivant.
11061  *
11062  * Fork - LGPL
11063  * <script type="text/javascript">
11064  */
11065
11066
11067
11068 /**
11069  * @class Roo.data.Store
11070  * @extends Roo.util.Observable
11071  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11072  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11073  * <p>
11074  * 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
11075  * has no knowledge of the format of the data returned by the Proxy.<br>
11076  * <p>
11077  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11078  * instances from the data object. These records are cached and made available through accessor functions.
11079  * @constructor
11080  * Creates a new Store.
11081  * @param {Object} config A config object containing the objects needed for the Store to access data,
11082  * and read the data into Records.
11083  */
11084 Roo.data.Store = function(config){
11085     this.data = new Roo.util.MixedCollection(false);
11086     this.data.getKey = function(o){
11087         return o.id;
11088     };
11089     this.baseParams = {};
11090     // private
11091     this.paramNames = {
11092         "start" : "start",
11093         "limit" : "limit",
11094         "sort" : "sort",
11095         "dir" : "dir",
11096         "multisort" : "_multisort"
11097     };
11098
11099     if(config && config.data){
11100         this.inlineData = config.data;
11101         delete config.data;
11102     }
11103
11104     Roo.apply(this, config);
11105     
11106     if(this.reader){ // reader passed
11107         this.reader = Roo.factory(this.reader, Roo.data);
11108         this.reader.xmodule = this.xmodule || false;
11109         if(!this.recordType){
11110             this.recordType = this.reader.recordType;
11111         }
11112         if(this.reader.onMetaChange){
11113             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11114         }
11115     }
11116
11117     if(this.recordType){
11118         this.fields = this.recordType.prototype.fields;
11119     }
11120     this.modified = [];
11121
11122     this.addEvents({
11123         /**
11124          * @event datachanged
11125          * Fires when the data cache has changed, and a widget which is using this Store
11126          * as a Record cache should refresh its view.
11127          * @param {Store} this
11128          */
11129         datachanged : true,
11130         /**
11131          * @event metachange
11132          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11133          * @param {Store} this
11134          * @param {Object} meta The JSON metadata
11135          */
11136         metachange : true,
11137         /**
11138          * @event add
11139          * Fires when Records have been added to the Store
11140          * @param {Store} this
11141          * @param {Roo.data.Record[]} records The array of Records added
11142          * @param {Number} index The index at which the record(s) were added
11143          */
11144         add : true,
11145         /**
11146          * @event remove
11147          * Fires when a Record has been removed from the Store
11148          * @param {Store} this
11149          * @param {Roo.data.Record} record The Record that was removed
11150          * @param {Number} index The index at which the record was removed
11151          */
11152         remove : true,
11153         /**
11154          * @event update
11155          * Fires when a Record has been updated
11156          * @param {Store} this
11157          * @param {Roo.data.Record} record The Record that was updated
11158          * @param {String} operation The update operation being performed.  Value may be one of:
11159          * <pre><code>
11160  Roo.data.Record.EDIT
11161  Roo.data.Record.REJECT
11162  Roo.data.Record.COMMIT
11163          * </code></pre>
11164          */
11165         update : true,
11166         /**
11167          * @event clear
11168          * Fires when the data cache has been cleared.
11169          * @param {Store} this
11170          */
11171         clear : true,
11172         /**
11173          * @event beforeload
11174          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11175          * the load action will be canceled.
11176          * @param {Store} this
11177          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11178          */
11179         beforeload : true,
11180         /**
11181          * @event beforeloadadd
11182          * Fires after a new set of Records has been loaded.
11183          * @param {Store} this
11184          * @param {Roo.data.Record[]} records The Records that were loaded
11185          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11186          */
11187         beforeloadadd : true,
11188         /**
11189          * @event load
11190          * Fires after a new set of Records has been loaded, before they are added to the store.
11191          * @param {Store} this
11192          * @param {Roo.data.Record[]} records The Records that were loaded
11193          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11194          * @params {Object} return from reader
11195          */
11196         load : true,
11197         /**
11198          * @event loadexception
11199          * Fires if an exception occurs in the Proxy during loading.
11200          * Called with the signature of the Proxy's "loadexception" event.
11201          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11202          * 
11203          * @param {Proxy} 
11204          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11205          * @param {Object} load options 
11206          * @param {Object} jsonData from your request (normally this contains the Exception)
11207          */
11208         loadexception : true
11209     });
11210     
11211     if(this.proxy){
11212         this.proxy = Roo.factory(this.proxy, Roo.data);
11213         this.proxy.xmodule = this.xmodule || false;
11214         this.relayEvents(this.proxy,  ["loadexception"]);
11215     }
11216     this.sortToggle = {};
11217     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11218
11219     Roo.data.Store.superclass.constructor.call(this);
11220
11221     if(this.inlineData){
11222         this.loadData(this.inlineData);
11223         delete this.inlineData;
11224     }
11225 };
11226
11227 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11228      /**
11229     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11230     * without a remote query - used by combo/forms at present.
11231     */
11232     
11233     /**
11234     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11235     */
11236     /**
11237     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11238     */
11239     /**
11240     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11241     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11242     */
11243     /**
11244     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11245     * on any HTTP request
11246     */
11247     /**
11248     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11249     */
11250     /**
11251     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11252     */
11253     multiSort: false,
11254     /**
11255     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11256     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11257     */
11258     remoteSort : false,
11259
11260     /**
11261     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11262      * loaded or when a record is removed. (defaults to false).
11263     */
11264     pruneModifiedRecords : false,
11265
11266     // private
11267     lastOptions : null,
11268
11269     /**
11270      * Add Records to the Store and fires the add event.
11271      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11272      */
11273     add : function(records){
11274         records = [].concat(records);
11275         for(var i = 0, len = records.length; i < len; i++){
11276             records[i].join(this);
11277         }
11278         var index = this.data.length;
11279         this.data.addAll(records);
11280         this.fireEvent("add", this, records, index);
11281     },
11282
11283     /**
11284      * Remove a Record from the Store and fires the remove event.
11285      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11286      */
11287     remove : function(record){
11288         var index = this.data.indexOf(record);
11289         this.data.removeAt(index);
11290  
11291         if(this.pruneModifiedRecords){
11292             this.modified.remove(record);
11293         }
11294         this.fireEvent("remove", this, record, index);
11295     },
11296
11297     /**
11298      * Remove all Records from the Store and fires the clear event.
11299      */
11300     removeAll : function(){
11301         this.data.clear();
11302         if(this.pruneModifiedRecords){
11303             this.modified = [];
11304         }
11305         this.fireEvent("clear", this);
11306     },
11307
11308     /**
11309      * Inserts Records to the Store at the given index and fires the add event.
11310      * @param {Number} index The start index at which to insert the passed Records.
11311      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11312      */
11313     insert : function(index, records){
11314         records = [].concat(records);
11315         for(var i = 0, len = records.length; i < len; i++){
11316             this.data.insert(index, records[i]);
11317             records[i].join(this);
11318         }
11319         this.fireEvent("add", this, records, index);
11320     },
11321
11322     /**
11323      * Get the index within the cache of the passed Record.
11324      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11325      * @return {Number} The index of the passed Record. Returns -1 if not found.
11326      */
11327     indexOf : function(record){
11328         return this.data.indexOf(record);
11329     },
11330
11331     /**
11332      * Get the index within the cache of the Record with the passed id.
11333      * @param {String} id The id of the Record to find.
11334      * @return {Number} The index of the Record. Returns -1 if not found.
11335      */
11336     indexOfId : function(id){
11337         return this.data.indexOfKey(id);
11338     },
11339
11340     /**
11341      * Get the Record with the specified id.
11342      * @param {String} id The id of the Record to find.
11343      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11344      */
11345     getById : function(id){
11346         return this.data.key(id);
11347     },
11348
11349     /**
11350      * Get the Record at the specified index.
11351      * @param {Number} index The index of the Record to find.
11352      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11353      */
11354     getAt : function(index){
11355         return this.data.itemAt(index);
11356     },
11357
11358     /**
11359      * Returns a range of Records between specified indices.
11360      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11361      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11362      * @return {Roo.data.Record[]} An array of Records
11363      */
11364     getRange : function(start, end){
11365         return this.data.getRange(start, end);
11366     },
11367
11368     // private
11369     storeOptions : function(o){
11370         o = Roo.apply({}, o);
11371         delete o.callback;
11372         delete o.scope;
11373         this.lastOptions = o;
11374     },
11375
11376     /**
11377      * Loads the Record cache from the configured Proxy using the configured Reader.
11378      * <p>
11379      * If using remote paging, then the first load call must specify the <em>start</em>
11380      * and <em>limit</em> properties in the options.params property to establish the initial
11381      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11382      * <p>
11383      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11384      * and this call will return before the new data has been loaded. Perform any post-processing
11385      * in a callback function, or in a "load" event handler.</strong>
11386      * <p>
11387      * @param {Object} options An object containing properties which control loading options:<ul>
11388      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11389      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11390      * passed the following arguments:<ul>
11391      * <li>r : Roo.data.Record[]</li>
11392      * <li>options: Options object from the load call</li>
11393      * <li>success: Boolean success indicator</li></ul></li>
11394      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11395      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11396      * </ul>
11397      */
11398     load : function(options){
11399         options = options || {};
11400         if(this.fireEvent("beforeload", this, options) !== false){
11401             this.storeOptions(options);
11402             var p = Roo.apply(options.params || {}, this.baseParams);
11403             // if meta was not loaded from remote source.. try requesting it.
11404             if (!this.reader.metaFromRemote) {
11405                 p._requestMeta = 1;
11406             }
11407             if(this.sortInfo && this.remoteSort){
11408                 var pn = this.paramNames;
11409                 p[pn["sort"]] = this.sortInfo.field;
11410                 p[pn["dir"]] = this.sortInfo.direction;
11411             }
11412             if (this.multiSort) {
11413                 var pn = this.paramNames;
11414                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11415             }
11416             
11417             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11418         }
11419     },
11420
11421     /**
11422      * Reloads the Record cache from the configured Proxy using the configured Reader and
11423      * the options from the last load operation performed.
11424      * @param {Object} options (optional) An object containing properties which may override the options
11425      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11426      * the most recently used options are reused).
11427      */
11428     reload : function(options){
11429         this.load(Roo.applyIf(options||{}, this.lastOptions));
11430     },
11431
11432     // private
11433     // Called as a callback by the Reader during a load operation.
11434     loadRecords : function(o, options, success){
11435         if(!o || success === false){
11436             if(success !== false){
11437                 this.fireEvent("load", this, [], options, o);
11438             }
11439             if(options.callback){
11440                 options.callback.call(options.scope || this, [], options, false);
11441             }
11442             return;
11443         }
11444         // if data returned failure - throw an exception.
11445         if (o.success === false) {
11446             // show a message if no listener is registered.
11447             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11448                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11449             }
11450             // loadmask wil be hooked into this..
11451             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11452             return;
11453         }
11454         var r = o.records, t = o.totalRecords || r.length;
11455         
11456         this.fireEvent("beforeloadadd", this, r, options, o);
11457         
11458         if(!options || options.add !== true){
11459             if(this.pruneModifiedRecords){
11460                 this.modified = [];
11461             }
11462             for(var i = 0, len = r.length; i < len; i++){
11463                 r[i].join(this);
11464             }
11465             if(this.snapshot){
11466                 this.data = this.snapshot;
11467                 delete this.snapshot;
11468             }
11469             this.data.clear();
11470             this.data.addAll(r);
11471             this.totalLength = t;
11472             this.applySort();
11473             this.fireEvent("datachanged", this);
11474         }else{
11475             this.totalLength = Math.max(t, this.data.length+r.length);
11476             this.add(r);
11477         }
11478         
11479         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11480                 
11481             var e = new Roo.data.Record({});
11482
11483             e.set(this.parent.displayField, this.parent.emptyTitle);
11484             e.set(this.parent.valueField, '');
11485
11486             this.insert(0, e);
11487         }
11488             
11489         this.fireEvent("load", this, r, options, o);
11490         if(options.callback){
11491             options.callback.call(options.scope || this, r, options, true);
11492         }
11493     },
11494
11495
11496     /**
11497      * Loads data from a passed data block. A Reader which understands the format of the data
11498      * must have been configured in the constructor.
11499      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11500      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11501      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11502      */
11503     loadData : function(o, append){
11504         var r = this.reader.readRecords(o);
11505         this.loadRecords(r, {add: append}, true);
11506     },
11507
11508     /**
11509      * Gets the number of cached records.
11510      * <p>
11511      * <em>If using paging, this may not be the total size of the dataset. If the data object
11512      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11513      * the data set size</em>
11514      */
11515     getCount : function(){
11516         return this.data.length || 0;
11517     },
11518
11519     /**
11520      * Gets the total number of records in the dataset as returned by the server.
11521      * <p>
11522      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11523      * the dataset size</em>
11524      */
11525     getTotalCount : function(){
11526         return this.totalLength || 0;
11527     },
11528
11529     /**
11530      * Returns the sort state of the Store as an object with two properties:
11531      * <pre><code>
11532  field {String} The name of the field by which the Records are sorted
11533  direction {String} The sort order, "ASC" or "DESC"
11534      * </code></pre>
11535      */
11536     getSortState : function(){
11537         return this.sortInfo;
11538     },
11539
11540     // private
11541     applySort : function(){
11542         if(this.sortInfo && !this.remoteSort){
11543             var s = this.sortInfo, f = s.field;
11544             var st = this.fields.get(f).sortType;
11545             var fn = function(r1, r2){
11546                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11547                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11548             };
11549             this.data.sort(s.direction, fn);
11550             if(this.snapshot && this.snapshot != this.data){
11551                 this.snapshot.sort(s.direction, fn);
11552             }
11553         }
11554     },
11555
11556     /**
11557      * Sets the default sort column and order to be used by the next load operation.
11558      * @param {String} fieldName The name of the field to sort by.
11559      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11560      */
11561     setDefaultSort : function(field, dir){
11562         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11563     },
11564
11565     /**
11566      * Sort the Records.
11567      * If remote sorting is used, the sort is performed on the server, and the cache is
11568      * reloaded. If local sorting is used, the cache is sorted internally.
11569      * @param {String} fieldName The name of the field to sort by.
11570      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11571      */
11572     sort : function(fieldName, dir){
11573         var f = this.fields.get(fieldName);
11574         if(!dir){
11575             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11576             
11577             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11578                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11579             }else{
11580                 dir = f.sortDir;
11581             }
11582         }
11583         this.sortToggle[f.name] = dir;
11584         this.sortInfo = {field: f.name, direction: dir};
11585         if(!this.remoteSort){
11586             this.applySort();
11587             this.fireEvent("datachanged", this);
11588         }else{
11589             this.load(this.lastOptions);
11590         }
11591     },
11592
11593     /**
11594      * Calls the specified function for each of the Records in the cache.
11595      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11596      * Returning <em>false</em> aborts and exits the iteration.
11597      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11598      */
11599     each : function(fn, scope){
11600         this.data.each(fn, scope);
11601     },
11602
11603     /**
11604      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11605      * (e.g., during paging).
11606      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11607      */
11608     getModifiedRecords : function(){
11609         return this.modified;
11610     },
11611
11612     // private
11613     createFilterFn : function(property, value, anyMatch){
11614         if(!value.exec){ // not a regex
11615             value = String(value);
11616             if(value.length == 0){
11617                 return false;
11618             }
11619             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11620         }
11621         return function(r){
11622             return value.test(r.data[property]);
11623         };
11624     },
11625
11626     /**
11627      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11628      * @param {String} property A field on your records
11629      * @param {Number} start The record index to start at (defaults to 0)
11630      * @param {Number} end The last record index to include (defaults to length - 1)
11631      * @return {Number} The sum
11632      */
11633     sum : function(property, start, end){
11634         var rs = this.data.items, v = 0;
11635         start = start || 0;
11636         end = (end || end === 0) ? end : rs.length-1;
11637
11638         for(var i = start; i <= end; i++){
11639             v += (rs[i].data[property] || 0);
11640         }
11641         return v;
11642     },
11643
11644     /**
11645      * Filter the records by a specified property.
11646      * @param {String} field A field on your records
11647      * @param {String/RegExp} value Either a string that the field
11648      * should start with or a RegExp to test against the field
11649      * @param {Boolean} anyMatch True to match any part not just the beginning
11650      */
11651     filter : function(property, value, anyMatch){
11652         var fn = this.createFilterFn(property, value, anyMatch);
11653         return fn ? this.filterBy(fn) : this.clearFilter();
11654     },
11655
11656     /**
11657      * Filter by a function. The specified function will be called with each
11658      * record in this data source. If the function returns true the record is included,
11659      * otherwise it is filtered.
11660      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11661      * @param {Object} scope (optional) The scope of the function (defaults to this)
11662      */
11663     filterBy : function(fn, scope){
11664         this.snapshot = this.snapshot || this.data;
11665         this.data = this.queryBy(fn, scope||this);
11666         this.fireEvent("datachanged", this);
11667     },
11668
11669     /**
11670      * Query the records by a specified property.
11671      * @param {String} field A field on your records
11672      * @param {String/RegExp} value Either a string that the field
11673      * should start with or a RegExp to test against the field
11674      * @param {Boolean} anyMatch True to match any part not just the beginning
11675      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11676      */
11677     query : function(property, value, anyMatch){
11678         var fn = this.createFilterFn(property, value, anyMatch);
11679         return fn ? this.queryBy(fn) : this.data.clone();
11680     },
11681
11682     /**
11683      * Query by a function. The specified function will be called with each
11684      * record in this data source. If the function returns true the record is included
11685      * in the results.
11686      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11687      * @param {Object} scope (optional) The scope of the function (defaults to this)
11688       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11689      **/
11690     queryBy : function(fn, scope){
11691         var data = this.snapshot || this.data;
11692         return data.filterBy(fn, scope||this);
11693     },
11694
11695     /**
11696      * Collects unique values for a particular dataIndex from this store.
11697      * @param {String} dataIndex The property to collect
11698      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11699      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11700      * @return {Array} An array of the unique values
11701      **/
11702     collect : function(dataIndex, allowNull, bypassFilter){
11703         var d = (bypassFilter === true && this.snapshot) ?
11704                 this.snapshot.items : this.data.items;
11705         var v, sv, r = [], l = {};
11706         for(var i = 0, len = d.length; i < len; i++){
11707             v = d[i].data[dataIndex];
11708             sv = String(v);
11709             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11710                 l[sv] = true;
11711                 r[r.length] = v;
11712             }
11713         }
11714         return r;
11715     },
11716
11717     /**
11718      * Revert to a view of the Record cache with no filtering applied.
11719      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11720      */
11721     clearFilter : function(suppressEvent){
11722         if(this.snapshot && this.snapshot != this.data){
11723             this.data = this.snapshot;
11724             delete this.snapshot;
11725             if(suppressEvent !== true){
11726                 this.fireEvent("datachanged", this);
11727             }
11728         }
11729     },
11730
11731     // private
11732     afterEdit : function(record){
11733         if(this.modified.indexOf(record) == -1){
11734             this.modified.push(record);
11735         }
11736         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11737     },
11738     
11739     // private
11740     afterReject : function(record){
11741         this.modified.remove(record);
11742         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11743     },
11744
11745     // private
11746     afterCommit : function(record){
11747         this.modified.remove(record);
11748         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11749     },
11750
11751     /**
11752      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11753      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11754      */
11755     commitChanges : function(){
11756         var m = this.modified.slice(0);
11757         this.modified = [];
11758         for(var i = 0, len = m.length; i < len; i++){
11759             m[i].commit();
11760         }
11761     },
11762
11763     /**
11764      * Cancel outstanding changes on all changed records.
11765      */
11766     rejectChanges : function(){
11767         var m = this.modified.slice(0);
11768         this.modified = [];
11769         for(var i = 0, len = m.length; i < len; i++){
11770             m[i].reject();
11771         }
11772     },
11773
11774     onMetaChange : function(meta, rtype, o){
11775         this.recordType = rtype;
11776         this.fields = rtype.prototype.fields;
11777         delete this.snapshot;
11778         this.sortInfo = meta.sortInfo || this.sortInfo;
11779         this.modified = [];
11780         this.fireEvent('metachange', this, this.reader.meta);
11781     },
11782     
11783     moveIndex : function(data, type)
11784     {
11785         var index = this.indexOf(data);
11786         
11787         var newIndex = index + type;
11788         
11789         this.remove(data);
11790         
11791         this.insert(newIndex, data);
11792         
11793     }
11794 });/*
11795  * Based on:
11796  * Ext JS Library 1.1.1
11797  * Copyright(c) 2006-2007, Ext JS, LLC.
11798  *
11799  * Originally Released Under LGPL - original licence link has changed is not relivant.
11800  *
11801  * Fork - LGPL
11802  * <script type="text/javascript">
11803  */
11804
11805 /**
11806  * @class Roo.data.SimpleStore
11807  * @extends Roo.data.Store
11808  * Small helper class to make creating Stores from Array data easier.
11809  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11810  * @cfg {Array} fields An array of field definition objects, or field name strings.
11811  * @cfg {Array} data The multi-dimensional array of data
11812  * @constructor
11813  * @param {Object} config
11814  */
11815 Roo.data.SimpleStore = function(config){
11816     Roo.data.SimpleStore.superclass.constructor.call(this, {
11817         isLocal : true,
11818         reader: new Roo.data.ArrayReader({
11819                 id: config.id
11820             },
11821             Roo.data.Record.create(config.fields)
11822         ),
11823         proxy : new Roo.data.MemoryProxy(config.data)
11824     });
11825     this.load();
11826 };
11827 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11828  * Based on:
11829  * Ext JS Library 1.1.1
11830  * Copyright(c) 2006-2007, Ext JS, LLC.
11831  *
11832  * Originally Released Under LGPL - original licence link has changed is not relivant.
11833  *
11834  * Fork - LGPL
11835  * <script type="text/javascript">
11836  */
11837
11838 /**
11839 /**
11840  * @extends Roo.data.Store
11841  * @class Roo.data.JsonStore
11842  * Small helper class to make creating Stores for JSON data easier. <br/>
11843 <pre><code>
11844 var store = new Roo.data.JsonStore({
11845     url: 'get-images.php',
11846     root: 'images',
11847     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11848 });
11849 </code></pre>
11850  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11851  * JsonReader and HttpProxy (unless inline data is provided).</b>
11852  * @cfg {Array} fields An array of field definition objects, or field name strings.
11853  * @constructor
11854  * @param {Object} config
11855  */
11856 Roo.data.JsonStore = function(c){
11857     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11858         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11859         reader: new Roo.data.JsonReader(c, c.fields)
11860     }));
11861 };
11862 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11863  * Based on:
11864  * Ext JS Library 1.1.1
11865  * Copyright(c) 2006-2007, Ext JS, LLC.
11866  *
11867  * Originally Released Under LGPL - original licence link has changed is not relivant.
11868  *
11869  * Fork - LGPL
11870  * <script type="text/javascript">
11871  */
11872
11873  
11874 Roo.data.Field = function(config){
11875     if(typeof config == "string"){
11876         config = {name: config};
11877     }
11878     Roo.apply(this, config);
11879     
11880     if(!this.type){
11881         this.type = "auto";
11882     }
11883     
11884     var st = Roo.data.SortTypes;
11885     // named sortTypes are supported, here we look them up
11886     if(typeof this.sortType == "string"){
11887         this.sortType = st[this.sortType];
11888     }
11889     
11890     // set default sortType for strings and dates
11891     if(!this.sortType){
11892         switch(this.type){
11893             case "string":
11894                 this.sortType = st.asUCString;
11895                 break;
11896             case "date":
11897                 this.sortType = st.asDate;
11898                 break;
11899             default:
11900                 this.sortType = st.none;
11901         }
11902     }
11903
11904     // define once
11905     var stripRe = /[\$,%]/g;
11906
11907     // prebuilt conversion function for this field, instead of
11908     // switching every time we're reading a value
11909     if(!this.convert){
11910         var cv, dateFormat = this.dateFormat;
11911         switch(this.type){
11912             case "":
11913             case "auto":
11914             case undefined:
11915                 cv = function(v){ return v; };
11916                 break;
11917             case "string":
11918                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11919                 break;
11920             case "int":
11921                 cv = function(v){
11922                     return v !== undefined && v !== null && v !== '' ?
11923                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11924                     };
11925                 break;
11926             case "float":
11927                 cv = function(v){
11928                     return v !== undefined && v !== null && v !== '' ?
11929                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11930                     };
11931                 break;
11932             case "bool":
11933             case "boolean":
11934                 cv = function(v){ return v === true || v === "true" || v == 1; };
11935                 break;
11936             case "date":
11937                 cv = function(v){
11938                     if(!v){
11939                         return '';
11940                     }
11941                     if(v instanceof Date){
11942                         return v;
11943                     }
11944                     if(dateFormat){
11945                         if(dateFormat == "timestamp"){
11946                             return new Date(v*1000);
11947                         }
11948                         return Date.parseDate(v, dateFormat);
11949                     }
11950                     var parsed = Date.parse(v);
11951                     return parsed ? new Date(parsed) : null;
11952                 };
11953              break;
11954             
11955         }
11956         this.convert = cv;
11957     }
11958 };
11959
11960 Roo.data.Field.prototype = {
11961     dateFormat: null,
11962     defaultValue: "",
11963     mapping: null,
11964     sortType : null,
11965     sortDir : "ASC"
11966 };/*
11967  * Based on:
11968  * Ext JS Library 1.1.1
11969  * Copyright(c) 2006-2007, Ext JS, LLC.
11970  *
11971  * Originally Released Under LGPL - original licence link has changed is not relivant.
11972  *
11973  * Fork - LGPL
11974  * <script type="text/javascript">
11975  */
11976  
11977 // Base class for reading structured data from a data source.  This class is intended to be
11978 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11979
11980 /**
11981  * @class Roo.data.DataReader
11982  * Base class for reading structured data from a data source.  This class is intended to be
11983  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11984  */
11985
11986 Roo.data.DataReader = function(meta, recordType){
11987     
11988     this.meta = meta;
11989     
11990     this.recordType = recordType instanceof Array ? 
11991         Roo.data.Record.create(recordType) : recordType;
11992 };
11993
11994 Roo.data.DataReader.prototype = {
11995      /**
11996      * Create an empty record
11997      * @param {Object} data (optional) - overlay some values
11998      * @return {Roo.data.Record} record created.
11999      */
12000     newRow :  function(d) {
12001         var da =  {};
12002         this.recordType.prototype.fields.each(function(c) {
12003             switch( c.type) {
12004                 case 'int' : da[c.name] = 0; break;
12005                 case 'date' : da[c.name] = new Date(); break;
12006                 case 'float' : da[c.name] = 0.0; break;
12007                 case 'boolean' : da[c.name] = false; break;
12008                 default : da[c.name] = ""; break;
12009             }
12010             
12011         });
12012         return new this.recordType(Roo.apply(da, d));
12013     }
12014     
12015 };/*
12016  * Based on:
12017  * Ext JS Library 1.1.1
12018  * Copyright(c) 2006-2007, Ext JS, LLC.
12019  *
12020  * Originally Released Under LGPL - original licence link has changed is not relivant.
12021  *
12022  * Fork - LGPL
12023  * <script type="text/javascript">
12024  */
12025
12026 /**
12027  * @class Roo.data.DataProxy
12028  * @extends Roo.data.Observable
12029  * This class is an abstract base class for implementations which provide retrieval of
12030  * unformatted data objects.<br>
12031  * <p>
12032  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12033  * (of the appropriate type which knows how to parse the data object) to provide a block of
12034  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12035  * <p>
12036  * Custom implementations must implement the load method as described in
12037  * {@link Roo.data.HttpProxy#load}.
12038  */
12039 Roo.data.DataProxy = function(){
12040     this.addEvents({
12041         /**
12042          * @event beforeload
12043          * Fires before a network request is made to retrieve a data object.
12044          * @param {Object} This DataProxy object.
12045          * @param {Object} params The params parameter to the load function.
12046          */
12047         beforeload : true,
12048         /**
12049          * @event load
12050          * Fires before the load method's callback is called.
12051          * @param {Object} This DataProxy object.
12052          * @param {Object} o The data object.
12053          * @param {Object} arg The callback argument object passed to the load function.
12054          */
12055         load : true,
12056         /**
12057          * @event loadexception
12058          * Fires if an Exception occurs during data retrieval.
12059          * @param {Object} This DataProxy object.
12060          * @param {Object} o The data object.
12061          * @param {Object} arg The callback argument object passed to the load function.
12062          * @param {Object} e The Exception.
12063          */
12064         loadexception : true
12065     });
12066     Roo.data.DataProxy.superclass.constructor.call(this);
12067 };
12068
12069 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12070
12071     /**
12072      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12073      */
12074 /*
12075  * Based on:
12076  * Ext JS Library 1.1.1
12077  * Copyright(c) 2006-2007, Ext JS, LLC.
12078  *
12079  * Originally Released Under LGPL - original licence link has changed is not relivant.
12080  *
12081  * Fork - LGPL
12082  * <script type="text/javascript">
12083  */
12084 /**
12085  * @class Roo.data.MemoryProxy
12086  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12087  * to the Reader when its load method is called.
12088  * @constructor
12089  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12090  */
12091 Roo.data.MemoryProxy = function(data){
12092     if (data.data) {
12093         data = data.data;
12094     }
12095     Roo.data.MemoryProxy.superclass.constructor.call(this);
12096     this.data = data;
12097 };
12098
12099 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12100     
12101     /**
12102      * Load data from the requested source (in this case an in-memory
12103      * data object passed to the constructor), read the data object into
12104      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12105      * process that block using the passed callback.
12106      * @param {Object} params This parameter is not used by the MemoryProxy class.
12107      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12108      * object into a block of Roo.data.Records.
12109      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12110      * The function must be passed <ul>
12111      * <li>The Record block object</li>
12112      * <li>The "arg" argument from the load function</li>
12113      * <li>A boolean success indicator</li>
12114      * </ul>
12115      * @param {Object} scope The scope in which to call the callback
12116      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12117      */
12118     load : function(params, reader, callback, scope, arg){
12119         params = params || {};
12120         var result;
12121         try {
12122             result = reader.readRecords(this.data);
12123         }catch(e){
12124             this.fireEvent("loadexception", this, arg, null, e);
12125             callback.call(scope, null, arg, false);
12126             return;
12127         }
12128         callback.call(scope, result, arg, true);
12129     },
12130     
12131     // private
12132     update : function(params, records){
12133         
12134     }
12135 });/*
12136  * Based on:
12137  * Ext JS Library 1.1.1
12138  * Copyright(c) 2006-2007, Ext JS, LLC.
12139  *
12140  * Originally Released Under LGPL - original licence link has changed is not relivant.
12141  *
12142  * Fork - LGPL
12143  * <script type="text/javascript">
12144  */
12145 /**
12146  * @class Roo.data.HttpProxy
12147  * @extends Roo.data.DataProxy
12148  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12149  * configured to reference a certain URL.<br><br>
12150  * <p>
12151  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12152  * from which the running page was served.<br><br>
12153  * <p>
12154  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12155  * <p>
12156  * Be aware that to enable the browser to parse an XML document, the server must set
12157  * the Content-Type header in the HTTP response to "text/xml".
12158  * @constructor
12159  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12160  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12161  * will be used to make the request.
12162  */
12163 Roo.data.HttpProxy = function(conn){
12164     Roo.data.HttpProxy.superclass.constructor.call(this);
12165     // is conn a conn config or a real conn?
12166     this.conn = conn;
12167     this.useAjax = !conn || !conn.events;
12168   
12169 };
12170
12171 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12172     // thse are take from connection...
12173     
12174     /**
12175      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12176      */
12177     /**
12178      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12179      * extra parameters to each request made by this object. (defaults to undefined)
12180      */
12181     /**
12182      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12183      *  to each request made by this object. (defaults to undefined)
12184      */
12185     /**
12186      * @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)
12187      */
12188     /**
12189      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12190      */
12191      /**
12192      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12193      * @type Boolean
12194      */
12195   
12196
12197     /**
12198      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12199      * @type Boolean
12200      */
12201     /**
12202      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12203      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12204      * a finer-grained basis than the DataProxy events.
12205      */
12206     getConnection : function(){
12207         return this.useAjax ? Roo.Ajax : this.conn;
12208     },
12209
12210     /**
12211      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12212      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12213      * process that block using the passed callback.
12214      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12215      * for the request to the remote server.
12216      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12217      * object into a block of Roo.data.Records.
12218      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12219      * The function must be passed <ul>
12220      * <li>The Record block object</li>
12221      * <li>The "arg" argument from the load function</li>
12222      * <li>A boolean success indicator</li>
12223      * </ul>
12224      * @param {Object} scope The scope in which to call the callback
12225      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12226      */
12227     load : function(params, reader, callback, scope, arg){
12228         if(this.fireEvent("beforeload", this, params) !== false){
12229             var  o = {
12230                 params : params || {},
12231                 request: {
12232                     callback : callback,
12233                     scope : scope,
12234                     arg : arg
12235                 },
12236                 reader: reader,
12237                 callback : this.loadResponse,
12238                 scope: this
12239             };
12240             if(this.useAjax){
12241                 Roo.applyIf(o, this.conn);
12242                 if(this.activeRequest){
12243                     Roo.Ajax.abort(this.activeRequest);
12244                 }
12245                 this.activeRequest = Roo.Ajax.request(o);
12246             }else{
12247                 this.conn.request(o);
12248             }
12249         }else{
12250             callback.call(scope||this, null, arg, false);
12251         }
12252     },
12253
12254     // private
12255     loadResponse : function(o, success, response){
12256         delete this.activeRequest;
12257         if(!success){
12258             this.fireEvent("loadexception", this, o, response);
12259             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12260             return;
12261         }
12262         var result;
12263         try {
12264             result = o.reader.read(response);
12265         }catch(e){
12266             this.fireEvent("loadexception", this, o, response, e);
12267             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12268             return;
12269         }
12270         
12271         this.fireEvent("load", this, o, o.request.arg);
12272         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12273     },
12274
12275     // private
12276     update : function(dataSet){
12277
12278     },
12279
12280     // private
12281     updateResponse : function(dataSet){
12282
12283     }
12284 });/*
12285  * Based on:
12286  * Ext JS Library 1.1.1
12287  * Copyright(c) 2006-2007, Ext JS, LLC.
12288  *
12289  * Originally Released Under LGPL - original licence link has changed is not relivant.
12290  *
12291  * Fork - LGPL
12292  * <script type="text/javascript">
12293  */
12294
12295 /**
12296  * @class Roo.data.ScriptTagProxy
12297  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12298  * other than the originating domain of the running page.<br><br>
12299  * <p>
12300  * <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
12301  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12302  * <p>
12303  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12304  * source code that is used as the source inside a &lt;script> tag.<br><br>
12305  * <p>
12306  * In order for the browser to process the returned data, the server must wrap the data object
12307  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12308  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12309  * depending on whether the callback name was passed:
12310  * <p>
12311  * <pre><code>
12312 boolean scriptTag = false;
12313 String cb = request.getParameter("callback");
12314 if (cb != null) {
12315     scriptTag = true;
12316     response.setContentType("text/javascript");
12317 } else {
12318     response.setContentType("application/x-json");
12319 }
12320 Writer out = response.getWriter();
12321 if (scriptTag) {
12322     out.write(cb + "(");
12323 }
12324 out.print(dataBlock.toJsonString());
12325 if (scriptTag) {
12326     out.write(");");
12327 }
12328 </pre></code>
12329  *
12330  * @constructor
12331  * @param {Object} config A configuration object.
12332  */
12333 Roo.data.ScriptTagProxy = function(config){
12334     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12335     Roo.apply(this, config);
12336     this.head = document.getElementsByTagName("head")[0];
12337 };
12338
12339 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12340
12341 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12342     /**
12343      * @cfg {String} url The URL from which to request the data object.
12344      */
12345     /**
12346      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12347      */
12348     timeout : 30000,
12349     /**
12350      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12351      * the server the name of the callback function set up by the load call to process the returned data object.
12352      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12353      * javascript output which calls this named function passing the data object as its only parameter.
12354      */
12355     callbackParam : "callback",
12356     /**
12357      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12358      * name to the request.
12359      */
12360     nocache : true,
12361
12362     /**
12363      * Load data from the configured URL, read the data object into
12364      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12365      * process that block using the passed callback.
12366      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12367      * for the request to the remote server.
12368      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12369      * object into a block of Roo.data.Records.
12370      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12371      * The function must be passed <ul>
12372      * <li>The Record block object</li>
12373      * <li>The "arg" argument from the load function</li>
12374      * <li>A boolean success indicator</li>
12375      * </ul>
12376      * @param {Object} scope The scope in which to call the callback
12377      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12378      */
12379     load : function(params, reader, callback, scope, arg){
12380         if(this.fireEvent("beforeload", this, params) !== false){
12381
12382             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12383
12384             var url = this.url;
12385             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12386             if(this.nocache){
12387                 url += "&_dc=" + (new Date().getTime());
12388             }
12389             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12390             var trans = {
12391                 id : transId,
12392                 cb : "stcCallback"+transId,
12393                 scriptId : "stcScript"+transId,
12394                 params : params,
12395                 arg : arg,
12396                 url : url,
12397                 callback : callback,
12398                 scope : scope,
12399                 reader : reader
12400             };
12401             var conn = this;
12402
12403             window[trans.cb] = function(o){
12404                 conn.handleResponse(o, trans);
12405             };
12406
12407             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12408
12409             if(this.autoAbort !== false){
12410                 this.abort();
12411             }
12412
12413             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12414
12415             var script = document.createElement("script");
12416             script.setAttribute("src", url);
12417             script.setAttribute("type", "text/javascript");
12418             script.setAttribute("id", trans.scriptId);
12419             this.head.appendChild(script);
12420
12421             this.trans = trans;
12422         }else{
12423             callback.call(scope||this, null, arg, false);
12424         }
12425     },
12426
12427     // private
12428     isLoading : function(){
12429         return this.trans ? true : false;
12430     },
12431
12432     /**
12433      * Abort the current server request.
12434      */
12435     abort : function(){
12436         if(this.isLoading()){
12437             this.destroyTrans(this.trans);
12438         }
12439     },
12440
12441     // private
12442     destroyTrans : function(trans, isLoaded){
12443         this.head.removeChild(document.getElementById(trans.scriptId));
12444         clearTimeout(trans.timeoutId);
12445         if(isLoaded){
12446             window[trans.cb] = undefined;
12447             try{
12448                 delete window[trans.cb];
12449             }catch(e){}
12450         }else{
12451             // if hasn't been loaded, wait for load to remove it to prevent script error
12452             window[trans.cb] = function(){
12453                 window[trans.cb] = undefined;
12454                 try{
12455                     delete window[trans.cb];
12456                 }catch(e){}
12457             };
12458         }
12459     },
12460
12461     // private
12462     handleResponse : function(o, trans){
12463         this.trans = false;
12464         this.destroyTrans(trans, true);
12465         var result;
12466         try {
12467             result = trans.reader.readRecords(o);
12468         }catch(e){
12469             this.fireEvent("loadexception", this, o, trans.arg, e);
12470             trans.callback.call(trans.scope||window, null, trans.arg, false);
12471             return;
12472         }
12473         this.fireEvent("load", this, o, trans.arg);
12474         trans.callback.call(trans.scope||window, result, trans.arg, true);
12475     },
12476
12477     // private
12478     handleFailure : function(trans){
12479         this.trans = false;
12480         this.destroyTrans(trans, false);
12481         this.fireEvent("loadexception", this, null, trans.arg);
12482         trans.callback.call(trans.scope||window, null, trans.arg, false);
12483     }
12484 });/*
12485  * Based on:
12486  * Ext JS Library 1.1.1
12487  * Copyright(c) 2006-2007, Ext JS, LLC.
12488  *
12489  * Originally Released Under LGPL - original licence link has changed is not relivant.
12490  *
12491  * Fork - LGPL
12492  * <script type="text/javascript">
12493  */
12494
12495 /**
12496  * @class Roo.data.JsonReader
12497  * @extends Roo.data.DataReader
12498  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12499  * based on mappings in a provided Roo.data.Record constructor.
12500  * 
12501  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12502  * in the reply previously. 
12503  * 
12504  * <p>
12505  * Example code:
12506  * <pre><code>
12507 var RecordDef = Roo.data.Record.create([
12508     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12509     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12510 ]);
12511 var myReader = new Roo.data.JsonReader({
12512     totalProperty: "results",    // The property which contains the total dataset size (optional)
12513     root: "rows",                // The property which contains an Array of row objects
12514     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12515 }, RecordDef);
12516 </code></pre>
12517  * <p>
12518  * This would consume a JSON file like this:
12519  * <pre><code>
12520 { 'results': 2, 'rows': [
12521     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12522     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12523 }
12524 </code></pre>
12525  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12526  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12527  * paged from the remote server.
12528  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12529  * @cfg {String} root name of the property which contains the Array of row objects.
12530  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12531  * @cfg {Array} fields Array of field definition objects
12532  * @constructor
12533  * Create a new JsonReader
12534  * @param {Object} meta Metadata configuration options
12535  * @param {Object} recordType Either an Array of field definition objects,
12536  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12537  */
12538 Roo.data.JsonReader = function(meta, recordType){
12539     
12540     meta = meta || {};
12541     // set some defaults:
12542     Roo.applyIf(meta, {
12543         totalProperty: 'total',
12544         successProperty : 'success',
12545         root : 'data',
12546         id : 'id'
12547     });
12548     
12549     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12550 };
12551 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12552     
12553     /**
12554      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12555      * Used by Store query builder to append _requestMeta to params.
12556      * 
12557      */
12558     metaFromRemote : false,
12559     /**
12560      * This method is only used by a DataProxy which has retrieved data from a remote server.
12561      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12562      * @return {Object} data A data block which is used by an Roo.data.Store object as
12563      * a cache of Roo.data.Records.
12564      */
12565     read : function(response){
12566         var json = response.responseText;
12567        
12568         var o = /* eval:var:o */ eval("("+json+")");
12569         if(!o) {
12570             throw {message: "JsonReader.read: Json object not found"};
12571         }
12572         
12573         if(o.metaData){
12574             
12575             delete this.ef;
12576             this.metaFromRemote = true;
12577             this.meta = o.metaData;
12578             this.recordType = Roo.data.Record.create(o.metaData.fields);
12579             this.onMetaChange(this.meta, this.recordType, o);
12580         }
12581         return this.readRecords(o);
12582     },
12583
12584     // private function a store will implement
12585     onMetaChange : function(meta, recordType, o){
12586
12587     },
12588
12589     /**
12590          * @ignore
12591          */
12592     simpleAccess: function(obj, subsc) {
12593         return obj[subsc];
12594     },
12595
12596         /**
12597          * @ignore
12598          */
12599     getJsonAccessor: function(){
12600         var re = /[\[\.]/;
12601         return function(expr) {
12602             try {
12603                 return(re.test(expr))
12604                     ? new Function("obj", "return obj." + expr)
12605                     : function(obj){
12606                         return obj[expr];
12607                     };
12608             } catch(e){}
12609             return Roo.emptyFn;
12610         };
12611     }(),
12612
12613     /**
12614      * Create a data block containing Roo.data.Records from an XML document.
12615      * @param {Object} o An object which contains an Array of row objects in the property specified
12616      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12617      * which contains the total size of the dataset.
12618      * @return {Object} data A data block which is used by an Roo.data.Store object as
12619      * a cache of Roo.data.Records.
12620      */
12621     readRecords : function(o){
12622         /**
12623          * After any data loads, the raw JSON data is available for further custom processing.
12624          * @type Object
12625          */
12626         this.o = o;
12627         var s = this.meta, Record = this.recordType,
12628             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12629
12630 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12631         if (!this.ef) {
12632             if(s.totalProperty) {
12633                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12634                 }
12635                 if(s.successProperty) {
12636                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12637                 }
12638                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12639                 if (s.id) {
12640                         var g = this.getJsonAccessor(s.id);
12641                         this.getId = function(rec) {
12642                                 var r = g(rec);  
12643                                 return (r === undefined || r === "") ? null : r;
12644                         };
12645                 } else {
12646                         this.getId = function(){return null;};
12647                 }
12648             this.ef = [];
12649             for(var jj = 0; jj < fl; jj++){
12650                 f = fi[jj];
12651                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12652                 this.ef[jj] = this.getJsonAccessor(map);
12653             }
12654         }
12655
12656         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12657         if(s.totalProperty){
12658             var vt = parseInt(this.getTotal(o), 10);
12659             if(!isNaN(vt)){
12660                 totalRecords = vt;
12661             }
12662         }
12663         if(s.successProperty){
12664             var vs = this.getSuccess(o);
12665             if(vs === false || vs === 'false'){
12666                 success = false;
12667             }
12668         }
12669         var records = [];
12670         for(var i = 0; i < c; i++){
12671                 var n = root[i];
12672             var values = {};
12673             var id = this.getId(n);
12674             for(var j = 0; j < fl; j++){
12675                 f = fi[j];
12676             var v = this.ef[j](n);
12677             if (!f.convert) {
12678                 Roo.log('missing convert for ' + f.name);
12679                 Roo.log(f);
12680                 continue;
12681             }
12682             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12683             }
12684             var record = new Record(values, id);
12685             record.json = n;
12686             records[i] = record;
12687         }
12688         return {
12689             raw : o,
12690             success : success,
12691             records : records,
12692             totalRecords : totalRecords
12693         };
12694     }
12695 });/*
12696  * Based on:
12697  * Ext JS Library 1.1.1
12698  * Copyright(c) 2006-2007, Ext JS, LLC.
12699  *
12700  * Originally Released Under LGPL - original licence link has changed is not relivant.
12701  *
12702  * Fork - LGPL
12703  * <script type="text/javascript">
12704  */
12705
12706 /**
12707  * @class Roo.data.ArrayReader
12708  * @extends Roo.data.DataReader
12709  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12710  * Each element of that Array represents a row of data fields. The
12711  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12712  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12713  * <p>
12714  * Example code:.
12715  * <pre><code>
12716 var RecordDef = Roo.data.Record.create([
12717     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12718     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12719 ]);
12720 var myReader = new Roo.data.ArrayReader({
12721     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12722 }, RecordDef);
12723 </code></pre>
12724  * <p>
12725  * This would consume an Array like this:
12726  * <pre><code>
12727 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12728   </code></pre>
12729  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12730  * @constructor
12731  * Create a new JsonReader
12732  * @param {Object} meta Metadata configuration options.
12733  * @param {Object} recordType Either an Array of field definition objects
12734  * as specified to {@link Roo.data.Record#create},
12735  * or an {@link Roo.data.Record} object
12736  * created using {@link Roo.data.Record#create}.
12737  */
12738 Roo.data.ArrayReader = function(meta, recordType){
12739     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12740 };
12741
12742 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12743     /**
12744      * Create a data block containing Roo.data.Records from an XML document.
12745      * @param {Object} o An Array of row objects which represents the dataset.
12746      * @return {Object} data A data block which is used by an Roo.data.Store object as
12747      * a cache of Roo.data.Records.
12748      */
12749     readRecords : function(o){
12750         var sid = this.meta ? this.meta.id : null;
12751         var recordType = this.recordType, fields = recordType.prototype.fields;
12752         var records = [];
12753         var root = o;
12754             for(var i = 0; i < root.length; i++){
12755                     var n = root[i];
12756                 var values = {};
12757                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12758                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12759                 var f = fields.items[j];
12760                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12761                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12762                 v = f.convert(v);
12763                 values[f.name] = v;
12764             }
12765                 var record = new recordType(values, id);
12766                 record.json = n;
12767                 records[records.length] = record;
12768             }
12769             return {
12770                 records : records,
12771                 totalRecords : records.length
12772             };
12773     }
12774 });/*
12775  * - LGPL
12776  * * 
12777  */
12778
12779 /**
12780  * @class Roo.bootstrap.ComboBox
12781  * @extends Roo.bootstrap.TriggerField
12782  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12783  * @cfg {Boolean} append (true|false) default false
12784  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12785  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12786  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12787  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12788  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12789  * @cfg {Boolean} animate default true
12790  * @cfg {Boolean} emptyResultText only for touch device
12791  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12792  * @cfg {String} emptyTitle default ''
12793  * @constructor
12794  * Create a new ComboBox.
12795  * @param {Object} config Configuration options
12796  */
12797 Roo.bootstrap.ComboBox = function(config){
12798     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12799     this.addEvents({
12800         /**
12801          * @event expand
12802          * Fires when the dropdown list is expanded
12803         * @param {Roo.bootstrap.ComboBox} combo This combo box
12804         */
12805         'expand' : true,
12806         /**
12807          * @event collapse
12808          * Fires when the dropdown list is collapsed
12809         * @param {Roo.bootstrap.ComboBox} combo This combo box
12810         */
12811         'collapse' : true,
12812         /**
12813          * @event beforeselect
12814          * Fires before a list item is selected. Return false to cancel the selection.
12815         * @param {Roo.bootstrap.ComboBox} combo This combo box
12816         * @param {Roo.data.Record} record The data record returned from the underlying store
12817         * @param {Number} index The index of the selected item in the dropdown list
12818         */
12819         'beforeselect' : true,
12820         /**
12821          * @event select
12822          * Fires when a list item is selected
12823         * @param {Roo.bootstrap.ComboBox} combo This combo box
12824         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12825         * @param {Number} index The index of the selected item in the dropdown list
12826         */
12827         'select' : true,
12828         /**
12829          * @event beforequery
12830          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12831          * The event object passed has these properties:
12832         * @param {Roo.bootstrap.ComboBox} combo This combo box
12833         * @param {String} query The query
12834         * @param {Boolean} forceAll true to force "all" query
12835         * @param {Boolean} cancel true to cancel the query
12836         * @param {Object} e The query event object
12837         */
12838         'beforequery': true,
12839          /**
12840          * @event add
12841          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12842         * @param {Roo.bootstrap.ComboBox} combo This combo box
12843         */
12844         'add' : true,
12845         /**
12846          * @event edit
12847          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12848         * @param {Roo.bootstrap.ComboBox} combo This combo box
12849         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12850         */
12851         'edit' : true,
12852         /**
12853          * @event remove
12854          * Fires when the remove value from the combobox array
12855         * @param {Roo.bootstrap.ComboBox} combo This combo box
12856         */
12857         'remove' : true,
12858         /**
12859          * @event afterremove
12860          * Fires when the remove value from the combobox array
12861         * @param {Roo.bootstrap.ComboBox} combo This combo box
12862         */
12863         'afterremove' : true,
12864         /**
12865          * @event specialfilter
12866          * Fires when specialfilter
12867             * @param {Roo.bootstrap.ComboBox} combo This combo box
12868             */
12869         'specialfilter' : true,
12870         /**
12871          * @event tick
12872          * Fires when tick the element
12873             * @param {Roo.bootstrap.ComboBox} combo This combo box
12874             */
12875         'tick' : true,
12876         /**
12877          * @event touchviewdisplay
12878          * Fires when touch view require special display (default is using displayField)
12879             * @param {Roo.bootstrap.ComboBox} combo This combo box
12880             * @param {Object} cfg set html .
12881             */
12882         'touchviewdisplay' : true
12883         
12884     });
12885     
12886     this.item = [];
12887     this.tickItems = [];
12888     
12889     this.selectedIndex = -1;
12890     if(this.mode == 'local'){
12891         if(config.queryDelay === undefined){
12892             this.queryDelay = 10;
12893         }
12894         if(config.minChars === undefined){
12895             this.minChars = 0;
12896         }
12897     }
12898 };
12899
12900 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12901      
12902     /**
12903      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12904      * rendering into an Roo.Editor, defaults to false)
12905      */
12906     /**
12907      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12908      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12909      */
12910     /**
12911      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12912      */
12913     /**
12914      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12915      * the dropdown list (defaults to undefined, with no header element)
12916      */
12917
12918      /**
12919      * @cfg {String/Roo.Template} tpl The template to use to render the output
12920      */
12921      
12922      /**
12923      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12924      */
12925     listWidth: undefined,
12926     /**
12927      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12928      * mode = 'remote' or 'text' if mode = 'local')
12929      */
12930     displayField: undefined,
12931     
12932     /**
12933      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12934      * mode = 'remote' or 'value' if mode = 'local'). 
12935      * Note: use of a valueField requires the user make a selection
12936      * in order for a value to be mapped.
12937      */
12938     valueField: undefined,
12939     /**
12940      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12941      */
12942     modalTitle : '',
12943     
12944     /**
12945      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12946      * field's data value (defaults to the underlying DOM element's name)
12947      */
12948     hiddenName: undefined,
12949     /**
12950      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12951      */
12952     listClass: '',
12953     /**
12954      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12955      */
12956     selectedClass: 'active',
12957     
12958     /**
12959      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12960      */
12961     shadow:'sides',
12962     /**
12963      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12964      * anchor positions (defaults to 'tl-bl')
12965      */
12966     listAlign: 'tl-bl?',
12967     /**
12968      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12969      */
12970     maxHeight: 300,
12971     /**
12972      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12973      * query specified by the allQuery config option (defaults to 'query')
12974      */
12975     triggerAction: 'query',
12976     /**
12977      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12978      * (defaults to 4, does not apply if editable = false)
12979      */
12980     minChars : 4,
12981     /**
12982      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12983      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12984      */
12985     typeAhead: false,
12986     /**
12987      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12988      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12989      */
12990     queryDelay: 500,
12991     /**
12992      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12993      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12994      */
12995     pageSize: 0,
12996     /**
12997      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12998      * when editable = true (defaults to false)
12999      */
13000     selectOnFocus:false,
13001     /**
13002      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13003      */
13004     queryParam: 'query',
13005     /**
13006      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13007      * when mode = 'remote' (defaults to 'Loading...')
13008      */
13009     loadingText: 'Loading...',
13010     /**
13011      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13012      */
13013     resizable: false,
13014     /**
13015      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13016      */
13017     handleHeight : 8,
13018     /**
13019      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13020      * traditional select (defaults to true)
13021      */
13022     editable: true,
13023     /**
13024      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13025      */
13026     allQuery: '',
13027     /**
13028      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13029      */
13030     mode: 'remote',
13031     /**
13032      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13033      * listWidth has a higher value)
13034      */
13035     minListWidth : 70,
13036     /**
13037      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13038      * allow the user to set arbitrary text into the field (defaults to false)
13039      */
13040     forceSelection:false,
13041     /**
13042      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13043      * if typeAhead = true (defaults to 250)
13044      */
13045     typeAheadDelay : 250,
13046     /**
13047      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13048      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13049      */
13050     valueNotFoundText : undefined,
13051     /**
13052      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13053      */
13054     blockFocus : false,
13055     
13056     /**
13057      * @cfg {Boolean} disableClear Disable showing of clear button.
13058      */
13059     disableClear : false,
13060     /**
13061      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13062      */
13063     alwaysQuery : false,
13064     
13065     /**
13066      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13067      */
13068     multiple : false,
13069     
13070     /**
13071      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13072      */
13073     invalidClass : "has-warning",
13074     
13075     /**
13076      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13077      */
13078     validClass : "has-success",
13079     
13080     /**
13081      * @cfg {Boolean} specialFilter (true|false) special filter default false
13082      */
13083     specialFilter : false,
13084     
13085     /**
13086      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13087      */
13088     mobileTouchView : true,
13089     
13090     /**
13091      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13092      */
13093     useNativeIOS : false,
13094     
13095     /**
13096      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13097      */
13098     mobile_restrict_height : false,
13099     
13100     ios_options : false,
13101     
13102     //private
13103     addicon : false,
13104     editicon: false,
13105     
13106     page: 0,
13107     hasQuery: false,
13108     append: false,
13109     loadNext: false,
13110     autoFocus : true,
13111     tickable : false,
13112     btnPosition : 'right',
13113     triggerList : true,
13114     showToggleBtn : true,
13115     animate : true,
13116     emptyResultText: 'Empty',
13117     triggerText : 'Select',
13118     emptyTitle : '',
13119     
13120     // element that contains real text value.. (when hidden is used..)
13121     
13122     getAutoCreate : function()
13123     {   
13124         var cfg = false;
13125         //render
13126         /*
13127          * Render classic select for iso
13128          */
13129         
13130         if(Roo.isIOS && this.useNativeIOS){
13131             cfg = this.getAutoCreateNativeIOS();
13132             return cfg;
13133         }
13134         
13135         /*
13136          * Touch Devices
13137          */
13138         
13139         if(Roo.isTouch && this.mobileTouchView){
13140             cfg = this.getAutoCreateTouchView();
13141             return cfg;;
13142         }
13143         
13144         /*
13145          *  Normal ComboBox
13146          */
13147         if(!this.tickable){
13148             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13149             return cfg;
13150         }
13151         
13152         /*
13153          *  ComboBox with tickable selections
13154          */
13155              
13156         var align = this.labelAlign || this.parentLabelAlign();
13157         
13158         cfg = {
13159             cls : 'form-group roo-combobox-tickable' //input-group
13160         };
13161         
13162         var btn_text_select = '';
13163         var btn_text_done = '';
13164         var btn_text_cancel = '';
13165         
13166         if (this.btn_text_show) {
13167             btn_text_select = 'Select';
13168             btn_text_done = 'Done';
13169             btn_text_cancel = 'Cancel'; 
13170         }
13171         
13172         var buttons = {
13173             tag : 'div',
13174             cls : 'tickable-buttons',
13175             cn : [
13176                 {
13177                     tag : 'button',
13178                     type : 'button',
13179                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13180                     //html : this.triggerText
13181                     html: btn_text_select
13182                 },
13183                 {
13184                     tag : 'button',
13185                     type : 'button',
13186                     name : 'ok',
13187                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13188                     //html : 'Done'
13189                     html: btn_text_done
13190                 },
13191                 {
13192                     tag : 'button',
13193                     type : 'button',
13194                     name : 'cancel',
13195                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13196                     //html : 'Cancel'
13197                     html: btn_text_cancel
13198                 }
13199             ]
13200         };
13201         
13202         if(this.editable){
13203             buttons.cn.unshift({
13204                 tag: 'input',
13205                 cls: 'roo-select2-search-field-input'
13206             });
13207         }
13208         
13209         var _this = this;
13210         
13211         Roo.each(buttons.cn, function(c){
13212             if (_this.size) {
13213                 c.cls += ' btn-' + _this.size;
13214             }
13215
13216             if (_this.disabled) {
13217                 c.disabled = true;
13218             }
13219         });
13220         
13221         var box = {
13222             tag: 'div',
13223             cn: [
13224                 {
13225                     tag: 'input',
13226                     type : 'hidden',
13227                     cls: 'form-hidden-field'
13228                 },
13229                 {
13230                     tag: 'ul',
13231                     cls: 'roo-select2-choices',
13232                     cn:[
13233                         {
13234                             tag: 'li',
13235                             cls: 'roo-select2-search-field',
13236                             cn: [
13237                                 buttons
13238                             ]
13239                         }
13240                     ]
13241                 }
13242             ]
13243         };
13244         
13245         var combobox = {
13246             cls: 'roo-select2-container input-group roo-select2-container-multi',
13247             cn: [
13248                 box
13249 //                {
13250 //                    tag: 'ul',
13251 //                    cls: 'typeahead typeahead-long dropdown-menu',
13252 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13253 //                }
13254             ]
13255         };
13256         
13257         if(this.hasFeedback && !this.allowBlank){
13258             
13259             var feedback = {
13260                 tag: 'span',
13261                 cls: 'glyphicon form-control-feedback'
13262             };
13263
13264             combobox.cn.push(feedback);
13265         }
13266         
13267         
13268         if (align ==='left' && this.fieldLabel.length) {
13269             
13270             cfg.cls += ' roo-form-group-label-left';
13271             
13272             cfg.cn = [
13273                 {
13274                     tag : 'i',
13275                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13276                     tooltip : 'This field is required'
13277                 },
13278                 {
13279                     tag: 'label',
13280                     'for' :  id,
13281                     cls : 'control-label',
13282                     html : this.fieldLabel
13283
13284                 },
13285                 {
13286                     cls : "", 
13287                     cn: [
13288                         combobox
13289                     ]
13290                 }
13291
13292             ];
13293             
13294             var labelCfg = cfg.cn[1];
13295             var contentCfg = cfg.cn[2];
13296             
13297
13298             if(this.indicatorpos == 'right'){
13299                 
13300                 cfg.cn = [
13301                     {
13302                         tag: 'label',
13303                         'for' :  id,
13304                         cls : 'control-label',
13305                         cn : [
13306                             {
13307                                 tag : 'span',
13308                                 html : this.fieldLabel
13309                             },
13310                             {
13311                                 tag : 'i',
13312                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13313                                 tooltip : 'This field is required'
13314                             }
13315                         ]
13316                     },
13317                     {
13318                         cls : "",
13319                         cn: [
13320                             combobox
13321                         ]
13322                     }
13323
13324                 ];
13325                 
13326                 
13327                 
13328                 labelCfg = cfg.cn[0];
13329                 contentCfg = cfg.cn[1];
13330             
13331             }
13332             
13333             if(this.labelWidth > 12){
13334                 labelCfg.style = "width: " + this.labelWidth + 'px';
13335             }
13336             
13337             if(this.labelWidth < 13 && this.labelmd == 0){
13338                 this.labelmd = this.labelWidth;
13339             }
13340             
13341             if(this.labellg > 0){
13342                 labelCfg.cls += ' col-lg-' + this.labellg;
13343                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13344             }
13345             
13346             if(this.labelmd > 0){
13347                 labelCfg.cls += ' col-md-' + this.labelmd;
13348                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13349             }
13350             
13351             if(this.labelsm > 0){
13352                 labelCfg.cls += ' col-sm-' + this.labelsm;
13353                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13354             }
13355             
13356             if(this.labelxs > 0){
13357                 labelCfg.cls += ' col-xs-' + this.labelxs;
13358                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13359             }
13360                 
13361                 
13362         } else if ( this.fieldLabel.length) {
13363 //                Roo.log(" label");
13364                  cfg.cn = [
13365                     {
13366                         tag : 'i',
13367                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13368                         tooltip : 'This field is required'
13369                     },
13370                     {
13371                         tag: 'label',
13372                         //cls : 'input-group-addon',
13373                         html : this.fieldLabel
13374                     },
13375                     combobox
13376                 ];
13377                 
13378                 if(this.indicatorpos == 'right'){
13379                     cfg.cn = [
13380                         {
13381                             tag: 'label',
13382                             //cls : 'input-group-addon',
13383                             html : this.fieldLabel
13384                         },
13385                         {
13386                             tag : 'i',
13387                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13388                             tooltip : 'This field is required'
13389                         },
13390                         combobox
13391                     ];
13392                     
13393                 }
13394
13395         } else {
13396             
13397 //                Roo.log(" no label && no align");
13398                 cfg = combobox
13399                      
13400                 
13401         }
13402          
13403         var settings=this;
13404         ['xs','sm','md','lg'].map(function(size){
13405             if (settings[size]) {
13406                 cfg.cls += ' col-' + size + '-' + settings[size];
13407             }
13408         });
13409         
13410         return cfg;
13411         
13412     },
13413     
13414     _initEventsCalled : false,
13415     
13416     // private
13417     initEvents: function()
13418     {   
13419         if (this._initEventsCalled) { // as we call render... prevent looping...
13420             return;
13421         }
13422         this._initEventsCalled = true;
13423         
13424         if (!this.store) {
13425             throw "can not find store for combo";
13426         }
13427         
13428         this.indicator = this.indicatorEl();
13429         
13430         this.store = Roo.factory(this.store, Roo.data);
13431         this.store.parent = this;
13432         
13433         // if we are building from html. then this element is so complex, that we can not really
13434         // use the rendered HTML.
13435         // so we have to trash and replace the previous code.
13436         if (Roo.XComponent.build_from_html) {
13437             // remove this element....
13438             var e = this.el.dom, k=0;
13439             while (e ) { e = e.previousSibling;  ++k;}
13440
13441             this.el.remove();
13442             
13443             this.el=false;
13444             this.rendered = false;
13445             
13446             this.render(this.parent().getChildContainer(true), k);
13447         }
13448         
13449         if(Roo.isIOS && this.useNativeIOS){
13450             this.initIOSView();
13451             return;
13452         }
13453         
13454         /*
13455          * Touch Devices
13456          */
13457         
13458         if(Roo.isTouch && this.mobileTouchView){
13459             this.initTouchView();
13460             return;
13461         }
13462         
13463         if(this.tickable){
13464             this.initTickableEvents();
13465             return;
13466         }
13467         
13468         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13469         
13470         if(this.hiddenName){
13471             
13472             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13473             
13474             this.hiddenField.dom.value =
13475                 this.hiddenValue !== undefined ? this.hiddenValue :
13476                 this.value !== undefined ? this.value : '';
13477
13478             // prevent input submission
13479             this.el.dom.removeAttribute('name');
13480             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13481              
13482              
13483         }
13484         //if(Roo.isGecko){
13485         //    this.el.dom.setAttribute('autocomplete', 'off');
13486         //}
13487         
13488         var cls = 'x-combo-list';
13489         
13490         //this.list = new Roo.Layer({
13491         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13492         //});
13493         
13494         var _this = this;
13495         
13496         (function(){
13497             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13498             _this.list.setWidth(lw);
13499         }).defer(100);
13500         
13501         this.list.on('mouseover', this.onViewOver, this);
13502         this.list.on('mousemove', this.onViewMove, this);
13503         this.list.on('scroll', this.onViewScroll, this);
13504         
13505         /*
13506         this.list.swallowEvent('mousewheel');
13507         this.assetHeight = 0;
13508
13509         if(this.title){
13510             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13511             this.assetHeight += this.header.getHeight();
13512         }
13513
13514         this.innerList = this.list.createChild({cls:cls+'-inner'});
13515         this.innerList.on('mouseover', this.onViewOver, this);
13516         this.innerList.on('mousemove', this.onViewMove, this);
13517         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13518         
13519         if(this.allowBlank && !this.pageSize && !this.disableClear){
13520             this.footer = this.list.createChild({cls:cls+'-ft'});
13521             this.pageTb = new Roo.Toolbar(this.footer);
13522            
13523         }
13524         if(this.pageSize){
13525             this.footer = this.list.createChild({cls:cls+'-ft'});
13526             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13527                     {pageSize: this.pageSize});
13528             
13529         }
13530         
13531         if (this.pageTb && this.allowBlank && !this.disableClear) {
13532             var _this = this;
13533             this.pageTb.add(new Roo.Toolbar.Fill(), {
13534                 cls: 'x-btn-icon x-btn-clear',
13535                 text: '&#160;',
13536                 handler: function()
13537                 {
13538                     _this.collapse();
13539                     _this.clearValue();
13540                     _this.onSelect(false, -1);
13541                 }
13542             });
13543         }
13544         if (this.footer) {
13545             this.assetHeight += this.footer.getHeight();
13546         }
13547         */
13548             
13549         if(!this.tpl){
13550             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13551         }
13552
13553         this.view = new Roo.View(this.list, this.tpl, {
13554             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13555         });
13556         //this.view.wrapEl.setDisplayed(false);
13557         this.view.on('click', this.onViewClick, this);
13558         
13559         
13560         this.store.on('beforeload', this.onBeforeLoad, this);
13561         this.store.on('load', this.onLoad, this);
13562         this.store.on('loadexception', this.onLoadException, this);
13563         /*
13564         if(this.resizable){
13565             this.resizer = new Roo.Resizable(this.list,  {
13566                pinned:true, handles:'se'
13567             });
13568             this.resizer.on('resize', function(r, w, h){
13569                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13570                 this.listWidth = w;
13571                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13572                 this.restrictHeight();
13573             }, this);
13574             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13575         }
13576         */
13577         if(!this.editable){
13578             this.editable = true;
13579             this.setEditable(false);
13580         }
13581         
13582         /*
13583         
13584         if (typeof(this.events.add.listeners) != 'undefined') {
13585             
13586             this.addicon = this.wrap.createChild(
13587                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13588        
13589             this.addicon.on('click', function(e) {
13590                 this.fireEvent('add', this);
13591             }, this);
13592         }
13593         if (typeof(this.events.edit.listeners) != 'undefined') {
13594             
13595             this.editicon = this.wrap.createChild(
13596                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13597             if (this.addicon) {
13598                 this.editicon.setStyle('margin-left', '40px');
13599             }
13600             this.editicon.on('click', function(e) {
13601                 
13602                 // we fire even  if inothing is selected..
13603                 this.fireEvent('edit', this, this.lastData );
13604                 
13605             }, this);
13606         }
13607         */
13608         
13609         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13610             "up" : function(e){
13611                 this.inKeyMode = true;
13612                 this.selectPrev();
13613             },
13614
13615             "down" : function(e){
13616                 if(!this.isExpanded()){
13617                     this.onTriggerClick();
13618                 }else{
13619                     this.inKeyMode = true;
13620                     this.selectNext();
13621                 }
13622             },
13623
13624             "enter" : function(e){
13625 //                this.onViewClick();
13626                 //return true;
13627                 this.collapse();
13628                 
13629                 if(this.fireEvent("specialkey", this, e)){
13630                     this.onViewClick(false);
13631                 }
13632                 
13633                 return true;
13634             },
13635
13636             "esc" : function(e){
13637                 this.collapse();
13638             },
13639
13640             "tab" : function(e){
13641                 this.collapse();
13642                 
13643                 if(this.fireEvent("specialkey", this, e)){
13644                     this.onViewClick(false);
13645                 }
13646                 
13647                 return true;
13648             },
13649
13650             scope : this,
13651
13652             doRelay : function(foo, bar, hname){
13653                 if(hname == 'down' || this.scope.isExpanded()){
13654                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13655                 }
13656                 return true;
13657             },
13658
13659             forceKeyDown: true
13660         });
13661         
13662         
13663         this.queryDelay = Math.max(this.queryDelay || 10,
13664                 this.mode == 'local' ? 10 : 250);
13665         
13666         
13667         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13668         
13669         if(this.typeAhead){
13670             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13671         }
13672         if(this.editable !== false){
13673             this.inputEl().on("keyup", this.onKeyUp, this);
13674         }
13675         if(this.forceSelection){
13676             this.inputEl().on('blur', this.doForce, this);
13677         }
13678         
13679         if(this.multiple){
13680             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13681             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13682         }
13683     },
13684     
13685     initTickableEvents: function()
13686     {   
13687         this.createList();
13688         
13689         if(this.hiddenName){
13690             
13691             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13692             
13693             this.hiddenField.dom.value =
13694                 this.hiddenValue !== undefined ? this.hiddenValue :
13695                 this.value !== undefined ? this.value : '';
13696
13697             // prevent input submission
13698             this.el.dom.removeAttribute('name');
13699             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13700              
13701              
13702         }
13703         
13704 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13705         
13706         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13707         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13708         if(this.triggerList){
13709             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13710         }
13711          
13712         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13713         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13714         
13715         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13716         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13717         
13718         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13719         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13720         
13721         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13722         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13723         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13724         
13725         this.okBtn.hide();
13726         this.cancelBtn.hide();
13727         
13728         var _this = this;
13729         
13730         (function(){
13731             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13732             _this.list.setWidth(lw);
13733         }).defer(100);
13734         
13735         this.list.on('mouseover', this.onViewOver, this);
13736         this.list.on('mousemove', this.onViewMove, this);
13737         
13738         this.list.on('scroll', this.onViewScroll, this);
13739         
13740         if(!this.tpl){
13741             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13742                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13743         }
13744
13745         this.view = new Roo.View(this.list, this.tpl, {
13746             singleSelect:true,
13747             tickable:true,
13748             parent:this,
13749             store: this.store,
13750             selectedClass: this.selectedClass
13751         });
13752         
13753         //this.view.wrapEl.setDisplayed(false);
13754         this.view.on('click', this.onViewClick, this);
13755         
13756         
13757         
13758         this.store.on('beforeload', this.onBeforeLoad, this);
13759         this.store.on('load', this.onLoad, this);
13760         this.store.on('loadexception', this.onLoadException, this);
13761         
13762         if(this.editable){
13763             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13764                 "up" : function(e){
13765                     this.inKeyMode = true;
13766                     this.selectPrev();
13767                 },
13768
13769                 "down" : function(e){
13770                     this.inKeyMode = true;
13771                     this.selectNext();
13772                 },
13773
13774                 "enter" : function(e){
13775                     if(this.fireEvent("specialkey", this, e)){
13776                         this.onViewClick(false);
13777                     }
13778                     
13779                     return true;
13780                 },
13781
13782                 "esc" : function(e){
13783                     this.onTickableFooterButtonClick(e, false, false);
13784                 },
13785
13786                 "tab" : function(e){
13787                     this.fireEvent("specialkey", this, e);
13788                     
13789                     this.onTickableFooterButtonClick(e, false, false);
13790                     
13791                     return true;
13792                 },
13793
13794                 scope : this,
13795
13796                 doRelay : function(e, fn, key){
13797                     if(this.scope.isExpanded()){
13798                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13799                     }
13800                     return true;
13801                 },
13802
13803                 forceKeyDown: true
13804             });
13805         }
13806         
13807         this.queryDelay = Math.max(this.queryDelay || 10,
13808                 this.mode == 'local' ? 10 : 250);
13809         
13810         
13811         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13812         
13813         if(this.typeAhead){
13814             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13815         }
13816         
13817         if(this.editable !== false){
13818             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13819         }
13820         
13821         this.indicator = this.indicatorEl();
13822         
13823         if(this.indicator){
13824             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13825             this.indicator.hide();
13826         }
13827         
13828     },
13829
13830     onDestroy : function(){
13831         if(this.view){
13832             this.view.setStore(null);
13833             this.view.el.removeAllListeners();
13834             this.view.el.remove();
13835             this.view.purgeListeners();
13836         }
13837         if(this.list){
13838             this.list.dom.innerHTML  = '';
13839         }
13840         
13841         if(this.store){
13842             this.store.un('beforeload', this.onBeforeLoad, this);
13843             this.store.un('load', this.onLoad, this);
13844             this.store.un('loadexception', this.onLoadException, this);
13845         }
13846         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13847     },
13848
13849     // private
13850     fireKey : function(e){
13851         if(e.isNavKeyPress() && !this.list.isVisible()){
13852             this.fireEvent("specialkey", this, e);
13853         }
13854     },
13855
13856     // private
13857     onResize: function(w, h){
13858 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13859 //        
13860 //        if(typeof w != 'number'){
13861 //            // we do not handle it!?!?
13862 //            return;
13863 //        }
13864 //        var tw = this.trigger.getWidth();
13865 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13866 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13867 //        var x = w - tw;
13868 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13869 //            
13870 //        //this.trigger.setStyle('left', x+'px');
13871 //        
13872 //        if(this.list && this.listWidth === undefined){
13873 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13874 //            this.list.setWidth(lw);
13875 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13876 //        }
13877         
13878     
13879         
13880     },
13881
13882     /**
13883      * Allow or prevent the user from directly editing the field text.  If false is passed,
13884      * the user will only be able to select from the items defined in the dropdown list.  This method
13885      * is the runtime equivalent of setting the 'editable' config option at config time.
13886      * @param {Boolean} value True to allow the user to directly edit the field text
13887      */
13888     setEditable : function(value){
13889         if(value == this.editable){
13890             return;
13891         }
13892         this.editable = value;
13893         if(!value){
13894             this.inputEl().dom.setAttribute('readOnly', true);
13895             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13896             this.inputEl().addClass('x-combo-noedit');
13897         }else{
13898             this.inputEl().dom.setAttribute('readOnly', false);
13899             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13900             this.inputEl().removeClass('x-combo-noedit');
13901         }
13902     },
13903
13904     // private
13905     
13906     onBeforeLoad : function(combo,opts){
13907         if(!this.hasFocus){
13908             return;
13909         }
13910          if (!opts.add) {
13911             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13912          }
13913         this.restrictHeight();
13914         this.selectedIndex = -1;
13915     },
13916
13917     // private
13918     onLoad : function(){
13919         
13920         this.hasQuery = false;
13921         
13922         if(!this.hasFocus){
13923             return;
13924         }
13925         
13926         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13927             this.loading.hide();
13928         }
13929         
13930         if(this.store.getCount() > 0){
13931             
13932             this.expand();
13933             this.restrictHeight();
13934             if(this.lastQuery == this.allQuery){
13935                 if(this.editable && !this.tickable){
13936                     this.inputEl().dom.select();
13937                 }
13938                 
13939                 if(
13940                     !this.selectByValue(this.value, true) &&
13941                     this.autoFocus && 
13942                     (
13943                         !this.store.lastOptions ||
13944                         typeof(this.store.lastOptions.add) == 'undefined' || 
13945                         this.store.lastOptions.add != true
13946                     )
13947                 ){
13948                     this.select(0, true);
13949                 }
13950             }else{
13951                 if(this.autoFocus){
13952                     this.selectNext();
13953                 }
13954                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13955                     this.taTask.delay(this.typeAheadDelay);
13956                 }
13957             }
13958         }else{
13959             this.onEmptyResults();
13960         }
13961         
13962         //this.el.focus();
13963     },
13964     // private
13965     onLoadException : function()
13966     {
13967         this.hasQuery = false;
13968         
13969         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13970             this.loading.hide();
13971         }
13972         
13973         if(this.tickable && this.editable){
13974             return;
13975         }
13976         
13977         this.collapse();
13978         // only causes errors at present
13979         //Roo.log(this.store.reader.jsonData);
13980         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13981             // fixme
13982             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13983         //}
13984         
13985         
13986     },
13987     // private
13988     onTypeAhead : function(){
13989         if(this.store.getCount() > 0){
13990             var r = this.store.getAt(0);
13991             var newValue = r.data[this.displayField];
13992             var len = newValue.length;
13993             var selStart = this.getRawValue().length;
13994             
13995             if(selStart != len){
13996                 this.setRawValue(newValue);
13997                 this.selectText(selStart, newValue.length);
13998             }
13999         }
14000     },
14001
14002     // private
14003     onSelect : function(record, index){
14004         
14005         if(this.fireEvent('beforeselect', this, record, index) !== false){
14006         
14007             this.setFromData(index > -1 ? record.data : false);
14008             
14009             this.collapse();
14010             this.fireEvent('select', this, record, index);
14011         }
14012     },
14013
14014     /**
14015      * Returns the currently selected field value or empty string if no value is set.
14016      * @return {String} value The selected value
14017      */
14018     getValue : function()
14019     {
14020         if(Roo.isIOS && this.useNativeIOS){
14021             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14022         }
14023         
14024         if(this.multiple){
14025             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14026         }
14027         
14028         if(this.valueField){
14029             return typeof this.value != 'undefined' ? this.value : '';
14030         }else{
14031             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14032         }
14033     },
14034     
14035     getRawValue : function()
14036     {
14037         if(Roo.isIOS && this.useNativeIOS){
14038             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14039         }
14040         
14041         var v = this.inputEl().getValue();
14042         
14043         return v;
14044     },
14045
14046     /**
14047      * Clears any text/value currently set in the field
14048      */
14049     clearValue : function(){
14050         
14051         if(this.hiddenField){
14052             this.hiddenField.dom.value = '';
14053         }
14054         this.value = '';
14055         this.setRawValue('');
14056         this.lastSelectionText = '';
14057         this.lastData = false;
14058         
14059         var close = this.closeTriggerEl();
14060         
14061         if(close){
14062             close.hide();
14063         }
14064         
14065         this.validate();
14066         
14067     },
14068
14069     /**
14070      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14071      * will be displayed in the field.  If the value does not match the data value of an existing item,
14072      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14073      * Otherwise the field will be blank (although the value will still be set).
14074      * @param {String} value The value to match
14075      */
14076     setValue : function(v)
14077     {
14078         if(Roo.isIOS && this.useNativeIOS){
14079             this.setIOSValue(v);
14080             return;
14081         }
14082         
14083         if(this.multiple){
14084             this.syncValue();
14085             return;
14086         }
14087         
14088         var text = v;
14089         if(this.valueField){
14090             var r = this.findRecord(this.valueField, v);
14091             if(r){
14092                 text = r.data[this.displayField];
14093             }else if(this.valueNotFoundText !== undefined){
14094                 text = this.valueNotFoundText;
14095             }
14096         }
14097         this.lastSelectionText = text;
14098         if(this.hiddenField){
14099             this.hiddenField.dom.value = v;
14100         }
14101         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14102         this.value = v;
14103         
14104         var close = this.closeTriggerEl();
14105         
14106         if(close){
14107             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14108         }
14109         
14110         this.validate();
14111     },
14112     /**
14113      * @property {Object} the last set data for the element
14114      */
14115     
14116     lastData : false,
14117     /**
14118      * Sets the value of the field based on a object which is related to the record format for the store.
14119      * @param {Object} value the value to set as. or false on reset?
14120      */
14121     setFromData : function(o){
14122         
14123         if(this.multiple){
14124             this.addItem(o);
14125             return;
14126         }
14127             
14128         var dv = ''; // display value
14129         var vv = ''; // value value..
14130         this.lastData = o;
14131         if (this.displayField) {
14132             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14133         } else {
14134             // this is an error condition!!!
14135             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14136         }
14137         
14138         if(this.valueField){
14139             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14140         }
14141         
14142         var close = this.closeTriggerEl();
14143         
14144         if(close){
14145             if(dv.length || vv * 1 > 0){
14146                 close.show() ;
14147                 this.blockFocus=true;
14148             } else {
14149                 close.hide();
14150             }             
14151         }
14152         
14153         if(this.hiddenField){
14154             this.hiddenField.dom.value = vv;
14155             
14156             this.lastSelectionText = dv;
14157             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14158             this.value = vv;
14159             return;
14160         }
14161         // no hidden field.. - we store the value in 'value', but still display
14162         // display field!!!!
14163         this.lastSelectionText = dv;
14164         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14165         this.value = vv;
14166         
14167         
14168         
14169     },
14170     // private
14171     reset : function(){
14172         // overridden so that last data is reset..
14173         
14174         if(this.multiple){
14175             this.clearItem();
14176             return;
14177         }
14178         
14179         this.setValue(this.originalValue);
14180         //this.clearInvalid();
14181         this.lastData = false;
14182         if (this.view) {
14183             this.view.clearSelections();
14184         }
14185         
14186         this.validate();
14187     },
14188     // private
14189     findRecord : function(prop, value){
14190         var record;
14191         if(this.store.getCount() > 0){
14192             this.store.each(function(r){
14193                 if(r.data[prop] == value){
14194                     record = r;
14195                     return false;
14196                 }
14197                 return true;
14198             });
14199         }
14200         return record;
14201     },
14202     
14203     getName: function()
14204     {
14205         // returns hidden if it's set..
14206         if (!this.rendered) {return ''};
14207         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14208         
14209     },
14210     // private
14211     onViewMove : function(e, t){
14212         this.inKeyMode = false;
14213     },
14214
14215     // private
14216     onViewOver : function(e, t){
14217         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14218             return;
14219         }
14220         var item = this.view.findItemFromChild(t);
14221         
14222         if(item){
14223             var index = this.view.indexOf(item);
14224             this.select(index, false);
14225         }
14226     },
14227
14228     // private
14229     onViewClick : function(view, doFocus, el, e)
14230     {
14231         var index = this.view.getSelectedIndexes()[0];
14232         
14233         var r = this.store.getAt(index);
14234         
14235         if(this.tickable){
14236             
14237             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14238                 return;
14239             }
14240             
14241             var rm = false;
14242             var _this = this;
14243             
14244             Roo.each(this.tickItems, function(v,k){
14245                 
14246                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14247                     Roo.log(v);
14248                     _this.tickItems.splice(k, 1);
14249                     
14250                     if(typeof(e) == 'undefined' && view == false){
14251                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14252                     }
14253                     
14254                     rm = true;
14255                     return;
14256                 }
14257             });
14258             
14259             if(rm){
14260                 return;
14261             }
14262             
14263             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14264                 this.tickItems.push(r.data);
14265             }
14266             
14267             if(typeof(e) == 'undefined' && view == false){
14268                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14269             }
14270                     
14271             return;
14272         }
14273         
14274         if(r){
14275             this.onSelect(r, index);
14276         }
14277         if(doFocus !== false && !this.blockFocus){
14278             this.inputEl().focus();
14279         }
14280     },
14281
14282     // private
14283     restrictHeight : function(){
14284         //this.innerList.dom.style.height = '';
14285         //var inner = this.innerList.dom;
14286         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14287         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14288         //this.list.beginUpdate();
14289         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14290         this.list.alignTo(this.inputEl(), this.listAlign);
14291         this.list.alignTo(this.inputEl(), this.listAlign);
14292         //this.list.endUpdate();
14293     },
14294
14295     // private
14296     onEmptyResults : function(){
14297         
14298         if(this.tickable && this.editable){
14299             this.hasFocus = false;
14300             this.restrictHeight();
14301             return;
14302         }
14303         
14304         this.collapse();
14305     },
14306
14307     /**
14308      * Returns true if the dropdown list is expanded, else false.
14309      */
14310     isExpanded : function(){
14311         return this.list.isVisible();
14312     },
14313
14314     /**
14315      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14316      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14317      * @param {String} value The data value of the item to select
14318      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14319      * selected item if it is not currently in view (defaults to true)
14320      * @return {Boolean} True if the value matched an item in the list, else false
14321      */
14322     selectByValue : function(v, scrollIntoView){
14323         if(v !== undefined && v !== null){
14324             var r = this.findRecord(this.valueField || this.displayField, v);
14325             if(r){
14326                 this.select(this.store.indexOf(r), scrollIntoView);
14327                 return true;
14328             }
14329         }
14330         return false;
14331     },
14332
14333     /**
14334      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14335      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14336      * @param {Number} index The zero-based index of the list item to select
14337      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14338      * selected item if it is not currently in view (defaults to true)
14339      */
14340     select : function(index, scrollIntoView){
14341         this.selectedIndex = index;
14342         this.view.select(index);
14343         if(scrollIntoView !== false){
14344             var el = this.view.getNode(index);
14345             /*
14346              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14347              */
14348             if(el){
14349                 this.list.scrollChildIntoView(el, false);
14350             }
14351         }
14352     },
14353
14354     // private
14355     selectNext : function(){
14356         var ct = this.store.getCount();
14357         if(ct > 0){
14358             if(this.selectedIndex == -1){
14359                 this.select(0);
14360             }else if(this.selectedIndex < ct-1){
14361                 this.select(this.selectedIndex+1);
14362             }
14363         }
14364     },
14365
14366     // private
14367     selectPrev : function(){
14368         var ct = this.store.getCount();
14369         if(ct > 0){
14370             if(this.selectedIndex == -1){
14371                 this.select(0);
14372             }else if(this.selectedIndex != 0){
14373                 this.select(this.selectedIndex-1);
14374             }
14375         }
14376     },
14377
14378     // private
14379     onKeyUp : function(e){
14380         if(this.editable !== false && !e.isSpecialKey()){
14381             this.lastKey = e.getKey();
14382             this.dqTask.delay(this.queryDelay);
14383         }
14384     },
14385
14386     // private
14387     validateBlur : function(){
14388         return !this.list || !this.list.isVisible();   
14389     },
14390
14391     // private
14392     initQuery : function(){
14393         
14394         var v = this.getRawValue();
14395         
14396         if(this.tickable && this.editable){
14397             v = this.tickableInputEl().getValue();
14398         }
14399         
14400         this.doQuery(v);
14401     },
14402
14403     // private
14404     doForce : function(){
14405         if(this.inputEl().dom.value.length > 0){
14406             this.inputEl().dom.value =
14407                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14408              
14409         }
14410     },
14411
14412     /**
14413      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14414      * query allowing the query action to be canceled if needed.
14415      * @param {String} query The SQL query to execute
14416      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14417      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14418      * saved in the current store (defaults to false)
14419      */
14420     doQuery : function(q, forceAll){
14421         
14422         if(q === undefined || q === null){
14423             q = '';
14424         }
14425         var qe = {
14426             query: q,
14427             forceAll: forceAll,
14428             combo: this,
14429             cancel:false
14430         };
14431         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14432             return false;
14433         }
14434         q = qe.query;
14435         
14436         forceAll = qe.forceAll;
14437         if(forceAll === true || (q.length >= this.minChars)){
14438             
14439             this.hasQuery = true;
14440             
14441             if(this.lastQuery != q || this.alwaysQuery){
14442                 this.lastQuery = q;
14443                 if(this.mode == 'local'){
14444                     this.selectedIndex = -1;
14445                     if(forceAll){
14446                         this.store.clearFilter();
14447                     }else{
14448                         
14449                         if(this.specialFilter){
14450                             this.fireEvent('specialfilter', this);
14451                             this.onLoad();
14452                             return;
14453                         }
14454                         
14455                         this.store.filter(this.displayField, q);
14456                     }
14457                     
14458                     this.store.fireEvent("datachanged", this.store);
14459                     
14460                     this.onLoad();
14461                     
14462                     
14463                 }else{
14464                     
14465                     this.store.baseParams[this.queryParam] = q;
14466                     
14467                     var options = {params : this.getParams(q)};
14468                     
14469                     if(this.loadNext){
14470                         options.add = true;
14471                         options.params.start = this.page * this.pageSize;
14472                     }
14473                     
14474                     this.store.load(options);
14475                     
14476                     /*
14477                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14478                      *  we should expand the list on onLoad
14479                      *  so command out it
14480                      */
14481 //                    this.expand();
14482                 }
14483             }else{
14484                 this.selectedIndex = -1;
14485                 this.onLoad();   
14486             }
14487         }
14488         
14489         this.loadNext = false;
14490     },
14491     
14492     // private
14493     getParams : function(q){
14494         var p = {};
14495         //p[this.queryParam] = q;
14496         
14497         if(this.pageSize){
14498             p.start = 0;
14499             p.limit = this.pageSize;
14500         }
14501         return p;
14502     },
14503
14504     /**
14505      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14506      */
14507     collapse : function(){
14508         if(!this.isExpanded()){
14509             return;
14510         }
14511         
14512         this.list.hide();
14513         
14514         this.hasFocus = false;
14515         
14516         if(this.tickable){
14517             this.okBtn.hide();
14518             this.cancelBtn.hide();
14519             this.trigger.show();
14520             
14521             if(this.editable){
14522                 this.tickableInputEl().dom.value = '';
14523                 this.tickableInputEl().blur();
14524             }
14525             
14526         }
14527         
14528         Roo.get(document).un('mousedown', this.collapseIf, this);
14529         Roo.get(document).un('mousewheel', this.collapseIf, this);
14530         if (!this.editable) {
14531             Roo.get(document).un('keydown', this.listKeyPress, this);
14532         }
14533         this.fireEvent('collapse', this);
14534         
14535         this.validate();
14536     },
14537
14538     // private
14539     collapseIf : function(e){
14540         var in_combo  = e.within(this.el);
14541         var in_list =  e.within(this.list);
14542         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14543         
14544         if (in_combo || in_list || is_list) {
14545             //e.stopPropagation();
14546             return;
14547         }
14548         
14549         if(this.tickable){
14550             this.onTickableFooterButtonClick(e, false, false);
14551         }
14552
14553         this.collapse();
14554         
14555     },
14556
14557     /**
14558      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14559      */
14560     expand : function(){
14561        
14562         if(this.isExpanded() || !this.hasFocus){
14563             return;
14564         }
14565         
14566         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14567         this.list.setWidth(lw);
14568         
14569         Roo.log('expand');
14570         
14571         this.list.show();
14572         
14573         this.restrictHeight();
14574         
14575         if(this.tickable){
14576             
14577             this.tickItems = Roo.apply([], this.item);
14578             
14579             this.okBtn.show();
14580             this.cancelBtn.show();
14581             this.trigger.hide();
14582             
14583             if(this.editable){
14584                 this.tickableInputEl().focus();
14585             }
14586             
14587         }
14588         
14589         Roo.get(document).on('mousedown', this.collapseIf, this);
14590         Roo.get(document).on('mousewheel', this.collapseIf, this);
14591         if (!this.editable) {
14592             Roo.get(document).on('keydown', this.listKeyPress, this);
14593         }
14594         
14595         this.fireEvent('expand', this);
14596     },
14597
14598     // private
14599     // Implements the default empty TriggerField.onTriggerClick function
14600     onTriggerClick : function(e)
14601     {
14602         Roo.log('trigger click');
14603         
14604         if(this.disabled || !this.triggerList){
14605             return;
14606         }
14607         
14608         this.page = 0;
14609         this.loadNext = false;
14610         
14611         if(this.isExpanded()){
14612             this.collapse();
14613             if (!this.blockFocus) {
14614                 this.inputEl().focus();
14615             }
14616             
14617         }else {
14618             this.hasFocus = true;
14619             if(this.triggerAction == 'all') {
14620                 this.doQuery(this.allQuery, true);
14621             } else {
14622                 this.doQuery(this.getRawValue());
14623             }
14624             if (!this.blockFocus) {
14625                 this.inputEl().focus();
14626             }
14627         }
14628     },
14629     
14630     onTickableTriggerClick : function(e)
14631     {
14632         if(this.disabled){
14633             return;
14634         }
14635         
14636         this.page = 0;
14637         this.loadNext = false;
14638         this.hasFocus = true;
14639         
14640         if(this.triggerAction == 'all') {
14641             this.doQuery(this.allQuery, true);
14642         } else {
14643             this.doQuery(this.getRawValue());
14644         }
14645     },
14646     
14647     onSearchFieldClick : function(e)
14648     {
14649         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14650             this.onTickableFooterButtonClick(e, false, false);
14651             return;
14652         }
14653         
14654         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14655             return;
14656         }
14657         
14658         this.page = 0;
14659         this.loadNext = false;
14660         this.hasFocus = true;
14661         
14662         if(this.triggerAction == 'all') {
14663             this.doQuery(this.allQuery, true);
14664         } else {
14665             this.doQuery(this.getRawValue());
14666         }
14667     },
14668     
14669     listKeyPress : function(e)
14670     {
14671         //Roo.log('listkeypress');
14672         // scroll to first matching element based on key pres..
14673         if (e.isSpecialKey()) {
14674             return false;
14675         }
14676         var k = String.fromCharCode(e.getKey()).toUpperCase();
14677         //Roo.log(k);
14678         var match  = false;
14679         var csel = this.view.getSelectedNodes();
14680         var cselitem = false;
14681         if (csel.length) {
14682             var ix = this.view.indexOf(csel[0]);
14683             cselitem  = this.store.getAt(ix);
14684             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14685                 cselitem = false;
14686             }
14687             
14688         }
14689         
14690         this.store.each(function(v) { 
14691             if (cselitem) {
14692                 // start at existing selection.
14693                 if (cselitem.id == v.id) {
14694                     cselitem = false;
14695                 }
14696                 return true;
14697             }
14698                 
14699             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14700                 match = this.store.indexOf(v);
14701                 return false;
14702             }
14703             return true;
14704         }, this);
14705         
14706         if (match === false) {
14707             return true; // no more action?
14708         }
14709         // scroll to?
14710         this.view.select(match);
14711         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14712         sn.scrollIntoView(sn.dom.parentNode, false);
14713     },
14714     
14715     onViewScroll : function(e, t){
14716         
14717         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){
14718             return;
14719         }
14720         
14721         this.hasQuery = true;
14722         
14723         this.loading = this.list.select('.loading', true).first();
14724         
14725         if(this.loading === null){
14726             this.list.createChild({
14727                 tag: 'div',
14728                 cls: 'loading roo-select2-more-results roo-select2-active',
14729                 html: 'Loading more results...'
14730             });
14731             
14732             this.loading = this.list.select('.loading', true).first();
14733             
14734             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14735             
14736             this.loading.hide();
14737         }
14738         
14739         this.loading.show();
14740         
14741         var _combo = this;
14742         
14743         this.page++;
14744         this.loadNext = true;
14745         
14746         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14747         
14748         return;
14749     },
14750     
14751     addItem : function(o)
14752     {   
14753         var dv = ''; // display value
14754         
14755         if (this.displayField) {
14756             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14757         } else {
14758             // this is an error condition!!!
14759             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14760         }
14761         
14762         if(!dv.length){
14763             return;
14764         }
14765         
14766         var choice = this.choices.createChild({
14767             tag: 'li',
14768             cls: 'roo-select2-search-choice',
14769             cn: [
14770                 {
14771                     tag: 'div',
14772                     html: dv
14773                 },
14774                 {
14775                     tag: 'a',
14776                     href: '#',
14777                     cls: 'roo-select2-search-choice-close fa fa-times',
14778                     tabindex: '-1'
14779                 }
14780             ]
14781             
14782         }, this.searchField);
14783         
14784         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14785         
14786         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14787         
14788         this.item.push(o);
14789         
14790         this.lastData = o;
14791         
14792         this.syncValue();
14793         
14794         this.inputEl().dom.value = '';
14795         
14796         this.validate();
14797     },
14798     
14799     onRemoveItem : function(e, _self, o)
14800     {
14801         e.preventDefault();
14802         
14803         this.lastItem = Roo.apply([], this.item);
14804         
14805         var index = this.item.indexOf(o.data) * 1;
14806         
14807         if( index < 0){
14808             Roo.log('not this item?!');
14809             return;
14810         }
14811         
14812         this.item.splice(index, 1);
14813         o.item.remove();
14814         
14815         this.syncValue();
14816         
14817         this.fireEvent('remove', this, e);
14818         
14819         this.validate();
14820         
14821     },
14822     
14823     syncValue : function()
14824     {
14825         if(!this.item.length){
14826             this.clearValue();
14827             return;
14828         }
14829             
14830         var value = [];
14831         var _this = this;
14832         Roo.each(this.item, function(i){
14833             if(_this.valueField){
14834                 value.push(i[_this.valueField]);
14835                 return;
14836             }
14837
14838             value.push(i);
14839         });
14840
14841         this.value = value.join(',');
14842
14843         if(this.hiddenField){
14844             this.hiddenField.dom.value = this.value;
14845         }
14846         
14847         this.store.fireEvent("datachanged", this.store);
14848         
14849         this.validate();
14850     },
14851     
14852     clearItem : function()
14853     {
14854         if(!this.multiple){
14855             return;
14856         }
14857         
14858         this.item = [];
14859         
14860         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14861            c.remove();
14862         });
14863         
14864         this.syncValue();
14865         
14866         this.validate();
14867         
14868         if(this.tickable && !Roo.isTouch){
14869             this.view.refresh();
14870         }
14871     },
14872     
14873     inputEl: function ()
14874     {
14875         if(Roo.isIOS && this.useNativeIOS){
14876             return this.el.select('select.roo-ios-select', true).first();
14877         }
14878         
14879         if(Roo.isTouch && this.mobileTouchView){
14880             return this.el.select('input.form-control',true).first();
14881         }
14882         
14883         if(this.tickable){
14884             return this.searchField;
14885         }
14886         
14887         return this.el.select('input.form-control',true).first();
14888     },
14889     
14890     onTickableFooterButtonClick : function(e, btn, el)
14891     {
14892         e.preventDefault();
14893         
14894         this.lastItem = Roo.apply([], this.item);
14895         
14896         if(btn && btn.name == 'cancel'){
14897             this.tickItems = Roo.apply([], this.item);
14898             this.collapse();
14899             return;
14900         }
14901         
14902         this.clearItem();
14903         
14904         var _this = this;
14905         
14906         Roo.each(this.tickItems, function(o){
14907             _this.addItem(o);
14908         });
14909         
14910         this.collapse();
14911         
14912     },
14913     
14914     validate : function()
14915     {
14916         if(this.getVisibilityEl().hasClass('hidden')){
14917             return true;
14918         }
14919         
14920         var v = this.getRawValue();
14921         
14922         if(this.multiple){
14923             v = this.getValue();
14924         }
14925         
14926         if(this.disabled || this.allowBlank || v.length){
14927             this.markValid();
14928             return true;
14929         }
14930         
14931         this.markInvalid();
14932         return false;
14933     },
14934     
14935     tickableInputEl : function()
14936     {
14937         if(!this.tickable || !this.editable){
14938             return this.inputEl();
14939         }
14940         
14941         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14942     },
14943     
14944     
14945     getAutoCreateTouchView : function()
14946     {
14947         var id = Roo.id();
14948         
14949         var cfg = {
14950             cls: 'form-group' //input-group
14951         };
14952         
14953         var input =  {
14954             tag: 'input',
14955             id : id,
14956             type : this.inputType,
14957             cls : 'form-control x-combo-noedit',
14958             autocomplete: 'new-password',
14959             placeholder : this.placeholder || '',
14960             readonly : true
14961         };
14962         
14963         if (this.name) {
14964             input.name = this.name;
14965         }
14966         
14967         if (this.size) {
14968             input.cls += ' input-' + this.size;
14969         }
14970         
14971         if (this.disabled) {
14972             input.disabled = true;
14973         }
14974         
14975         var inputblock = {
14976             cls : '',
14977             cn : [
14978                 input
14979             ]
14980         };
14981         
14982         if(this.before){
14983             inputblock.cls += ' input-group';
14984             
14985             inputblock.cn.unshift({
14986                 tag :'span',
14987                 cls : 'input-group-addon',
14988                 html : this.before
14989             });
14990         }
14991         
14992         if(this.removable && !this.multiple){
14993             inputblock.cls += ' roo-removable';
14994             
14995             inputblock.cn.push({
14996                 tag: 'button',
14997                 html : 'x',
14998                 cls : 'roo-combo-removable-btn close'
14999             });
15000         }
15001
15002         if(this.hasFeedback && !this.allowBlank){
15003             
15004             inputblock.cls += ' has-feedback';
15005             
15006             inputblock.cn.push({
15007                 tag: 'span',
15008                 cls: 'glyphicon form-control-feedback'
15009             });
15010             
15011         }
15012         
15013         if (this.after) {
15014             
15015             inputblock.cls += (this.before) ? '' : ' input-group';
15016             
15017             inputblock.cn.push({
15018                 tag :'span',
15019                 cls : 'input-group-addon',
15020                 html : this.after
15021             });
15022         }
15023
15024         var box = {
15025             tag: 'div',
15026             cn: [
15027                 {
15028                     tag: 'input',
15029                     type : 'hidden',
15030                     cls: 'form-hidden-field'
15031                 },
15032                 inputblock
15033             ]
15034             
15035         };
15036         
15037         if(this.multiple){
15038             box = {
15039                 tag: 'div',
15040                 cn: [
15041                     {
15042                         tag: 'input',
15043                         type : 'hidden',
15044                         cls: 'form-hidden-field'
15045                     },
15046                     {
15047                         tag: 'ul',
15048                         cls: 'roo-select2-choices',
15049                         cn:[
15050                             {
15051                                 tag: 'li',
15052                                 cls: 'roo-select2-search-field',
15053                                 cn: [
15054
15055                                     inputblock
15056                                 ]
15057                             }
15058                         ]
15059                     }
15060                 ]
15061             }
15062         };
15063         
15064         var combobox = {
15065             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15066             cn: [
15067                 box
15068             ]
15069         };
15070         
15071         if(!this.multiple && this.showToggleBtn){
15072             
15073             var caret = {
15074                         tag: 'span',
15075                         cls: 'caret'
15076             };
15077             
15078             if (this.caret != false) {
15079                 caret = {
15080                      tag: 'i',
15081                      cls: 'fa fa-' + this.caret
15082                 };
15083                 
15084             }
15085             
15086             combobox.cn.push({
15087                 tag :'span',
15088                 cls : 'input-group-addon btn dropdown-toggle',
15089                 cn : [
15090                     caret,
15091                     {
15092                         tag: 'span',
15093                         cls: 'combobox-clear',
15094                         cn  : [
15095                             {
15096                                 tag : 'i',
15097                                 cls: 'icon-remove'
15098                             }
15099                         ]
15100                     }
15101                 ]
15102
15103             })
15104         }
15105         
15106         if(this.multiple){
15107             combobox.cls += ' roo-select2-container-multi';
15108         }
15109         
15110         var align = this.labelAlign || this.parentLabelAlign();
15111         
15112         if (align ==='left' && this.fieldLabel.length) {
15113
15114             cfg.cn = [
15115                 {
15116                    tag : 'i',
15117                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15118                    tooltip : 'This field is required'
15119                 },
15120                 {
15121                     tag: 'label',
15122                     cls : 'control-label',
15123                     html : this.fieldLabel
15124
15125                 },
15126                 {
15127                     cls : '', 
15128                     cn: [
15129                         combobox
15130                     ]
15131                 }
15132             ];
15133             
15134             var labelCfg = cfg.cn[1];
15135             var contentCfg = cfg.cn[2];
15136             
15137
15138             if(this.indicatorpos == 'right'){
15139                 cfg.cn = [
15140                     {
15141                         tag: 'label',
15142                         'for' :  id,
15143                         cls : 'control-label',
15144                         cn : [
15145                             {
15146                                 tag : 'span',
15147                                 html : this.fieldLabel
15148                             },
15149                             {
15150                                 tag : 'i',
15151                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15152                                 tooltip : 'This field is required'
15153                             }
15154                         ]
15155                     },
15156                     {
15157                         cls : "",
15158                         cn: [
15159                             combobox
15160                         ]
15161                     }
15162
15163                 ];
15164                 
15165                 labelCfg = cfg.cn[0];
15166                 contentCfg = cfg.cn[1];
15167             }
15168             
15169            
15170             
15171             if(this.labelWidth > 12){
15172                 labelCfg.style = "width: " + this.labelWidth + 'px';
15173             }
15174             
15175             if(this.labelWidth < 13 && this.labelmd == 0){
15176                 this.labelmd = this.labelWidth;
15177             }
15178             
15179             if(this.labellg > 0){
15180                 labelCfg.cls += ' col-lg-' + this.labellg;
15181                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15182             }
15183             
15184             if(this.labelmd > 0){
15185                 labelCfg.cls += ' col-md-' + this.labelmd;
15186                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15187             }
15188             
15189             if(this.labelsm > 0){
15190                 labelCfg.cls += ' col-sm-' + this.labelsm;
15191                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15192             }
15193             
15194             if(this.labelxs > 0){
15195                 labelCfg.cls += ' col-xs-' + this.labelxs;
15196                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15197             }
15198                 
15199                 
15200         } else if ( this.fieldLabel.length) {
15201             cfg.cn = [
15202                 {
15203                    tag : 'i',
15204                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15205                    tooltip : 'This field is required'
15206                 },
15207                 {
15208                     tag: 'label',
15209                     cls : 'control-label',
15210                     html : this.fieldLabel
15211
15212                 },
15213                 {
15214                     cls : '', 
15215                     cn: [
15216                         combobox
15217                     ]
15218                 }
15219             ];
15220             
15221             if(this.indicatorpos == 'right'){
15222                 cfg.cn = [
15223                     {
15224                         tag: 'label',
15225                         cls : 'control-label',
15226                         html : this.fieldLabel,
15227                         cn : [
15228                             {
15229                                tag : 'i',
15230                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15231                                tooltip : 'This field is required'
15232                             }
15233                         ]
15234                     },
15235                     {
15236                         cls : '', 
15237                         cn: [
15238                             combobox
15239                         ]
15240                     }
15241                 ];
15242             }
15243         } else {
15244             cfg.cn = combobox;    
15245         }
15246         
15247         
15248         var settings = this;
15249         
15250         ['xs','sm','md','lg'].map(function(size){
15251             if (settings[size]) {
15252                 cfg.cls += ' col-' + size + '-' + settings[size];
15253             }
15254         });
15255         
15256         return cfg;
15257     },
15258     
15259     initTouchView : function()
15260     {
15261         this.renderTouchView();
15262         
15263         this.touchViewEl.on('scroll', function(){
15264             this.el.dom.scrollTop = 0;
15265         }, this);
15266         
15267         this.originalValue = this.getValue();
15268         
15269         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15270         
15271         this.inputEl().on("click", this.showTouchView, this);
15272         if (this.triggerEl) {
15273             this.triggerEl.on("click", this.showTouchView, this);
15274         }
15275         
15276         
15277         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15278         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15279         
15280         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15281         
15282         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15283         this.store.on('load', this.onTouchViewLoad, this);
15284         this.store.on('loadexception', this.onTouchViewLoadException, this);
15285         
15286         if(this.hiddenName){
15287             
15288             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15289             
15290             this.hiddenField.dom.value =
15291                 this.hiddenValue !== undefined ? this.hiddenValue :
15292                 this.value !== undefined ? this.value : '';
15293         
15294             this.el.dom.removeAttribute('name');
15295             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15296         }
15297         
15298         if(this.multiple){
15299             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15300             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15301         }
15302         
15303         if(this.removable && !this.multiple){
15304             var close = this.closeTriggerEl();
15305             if(close){
15306                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15307                 close.on('click', this.removeBtnClick, this, close);
15308             }
15309         }
15310         /*
15311          * fix the bug in Safari iOS8
15312          */
15313         this.inputEl().on("focus", function(e){
15314             document.activeElement.blur();
15315         }, this);
15316         
15317         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15318         
15319         return;
15320         
15321         
15322     },
15323     
15324     renderTouchView : function()
15325     {
15326         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15327         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15328         
15329         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15330         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15331         
15332         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15333         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15334         this.touchViewBodyEl.setStyle('overflow', 'auto');
15335         
15336         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15337         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15338         
15339         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15340         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15341         
15342     },
15343     
15344     showTouchView : function()
15345     {
15346         if(this.disabled){
15347             return;
15348         }
15349         
15350         this.touchViewHeaderEl.hide();
15351
15352         if(this.modalTitle.length){
15353             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15354             this.touchViewHeaderEl.show();
15355         }
15356
15357         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15358         this.touchViewEl.show();
15359
15360         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15361         
15362         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15363         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15364
15365         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15366
15367         if(this.modalTitle.length){
15368             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15369         }
15370         
15371         this.touchViewBodyEl.setHeight(bodyHeight);
15372
15373         if(this.animate){
15374             var _this = this;
15375             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15376         }else{
15377             this.touchViewEl.addClass('in');
15378         }
15379         
15380         if(this._touchViewMask){
15381             Roo.get(document.body).addClass("x-body-masked");
15382             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15383             this._touchViewMask.setStyle('z-index', 10000);
15384             this._touchViewMask.addClass('show');
15385         }
15386         
15387         this.doTouchViewQuery();
15388         
15389     },
15390     
15391     hideTouchView : function()
15392     {
15393         this.touchViewEl.removeClass('in');
15394
15395         if(this.animate){
15396             var _this = this;
15397             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15398         }else{
15399             this.touchViewEl.setStyle('display', 'none');
15400         }
15401         
15402         if(this._touchViewMask){
15403             this._touchViewMask.removeClass('show');
15404             Roo.get(document.body).removeClass("x-body-masked");
15405         }
15406     },
15407     
15408     setTouchViewValue : function()
15409     {
15410         if(this.multiple){
15411             this.clearItem();
15412         
15413             var _this = this;
15414
15415             Roo.each(this.tickItems, function(o){
15416                 this.addItem(o);
15417             }, this);
15418         }
15419         
15420         this.hideTouchView();
15421     },
15422     
15423     doTouchViewQuery : function()
15424     {
15425         var qe = {
15426             query: '',
15427             forceAll: true,
15428             combo: this,
15429             cancel:false
15430         };
15431         
15432         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15433             return false;
15434         }
15435         
15436         if(!this.alwaysQuery || this.mode == 'local'){
15437             this.onTouchViewLoad();
15438             return;
15439         }
15440         
15441         this.store.load();
15442     },
15443     
15444     onTouchViewBeforeLoad : function(combo,opts)
15445     {
15446         return;
15447     },
15448
15449     // private
15450     onTouchViewLoad : function()
15451     {
15452         if(this.store.getCount() < 1){
15453             this.onTouchViewEmptyResults();
15454             return;
15455         }
15456         
15457         this.clearTouchView();
15458         
15459         var rawValue = this.getRawValue();
15460         
15461         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15462         
15463         this.tickItems = [];
15464         
15465         this.store.data.each(function(d, rowIndex){
15466             var row = this.touchViewListGroup.createChild(template);
15467             
15468             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15469                 row.addClass(d.data.cls);
15470             }
15471             
15472             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15473                 var cfg = {
15474                     data : d.data,
15475                     html : d.data[this.displayField]
15476                 };
15477                 
15478                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15479                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15480                 }
15481             }
15482             row.removeClass('selected');
15483             if(!this.multiple && this.valueField &&
15484                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15485             {
15486                 // radio buttons..
15487                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15488                 row.addClass('selected');
15489             }
15490             
15491             if(this.multiple && this.valueField &&
15492                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15493             {
15494                 
15495                 // checkboxes...
15496                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15497                 this.tickItems.push(d.data);
15498             }
15499             
15500             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15501             
15502         }, this);
15503         
15504         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15505         
15506         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15507
15508         if(this.modalTitle.length){
15509             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15510         }
15511
15512         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15513         
15514         if(this.mobile_restrict_height && listHeight < bodyHeight){
15515             this.touchViewBodyEl.setHeight(listHeight);
15516         }
15517         
15518         var _this = this;
15519         
15520         if(firstChecked && listHeight > bodyHeight){
15521             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15522         }
15523         
15524     },
15525     
15526     onTouchViewLoadException : function()
15527     {
15528         this.hideTouchView();
15529     },
15530     
15531     onTouchViewEmptyResults : function()
15532     {
15533         this.clearTouchView();
15534         
15535         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15536         
15537         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15538         
15539     },
15540     
15541     clearTouchView : function()
15542     {
15543         this.touchViewListGroup.dom.innerHTML = '';
15544     },
15545     
15546     onTouchViewClick : function(e, el, o)
15547     {
15548         e.preventDefault();
15549         
15550         var row = o.row;
15551         var rowIndex = o.rowIndex;
15552         
15553         var r = this.store.getAt(rowIndex);
15554         
15555         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15556             
15557             if(!this.multiple){
15558                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15559                     c.dom.removeAttribute('checked');
15560                 }, this);
15561
15562                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15563
15564                 this.setFromData(r.data);
15565
15566                 var close = this.closeTriggerEl();
15567
15568                 if(close){
15569                     close.show();
15570                 }
15571
15572                 this.hideTouchView();
15573
15574                 this.fireEvent('select', this, r, rowIndex);
15575
15576                 return;
15577             }
15578
15579             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15580                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15581                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15582                 return;
15583             }
15584
15585             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15586             this.addItem(r.data);
15587             this.tickItems.push(r.data);
15588         }
15589     },
15590     
15591     getAutoCreateNativeIOS : function()
15592     {
15593         var cfg = {
15594             cls: 'form-group' //input-group,
15595         };
15596         
15597         var combobox =  {
15598             tag: 'select',
15599             cls : 'roo-ios-select'
15600         };
15601         
15602         if (this.name) {
15603             combobox.name = this.name;
15604         }
15605         
15606         if (this.disabled) {
15607             combobox.disabled = true;
15608         }
15609         
15610         var settings = this;
15611         
15612         ['xs','sm','md','lg'].map(function(size){
15613             if (settings[size]) {
15614                 cfg.cls += ' col-' + size + '-' + settings[size];
15615             }
15616         });
15617         
15618         cfg.cn = combobox;
15619         
15620         return cfg;
15621         
15622     },
15623     
15624     initIOSView : function()
15625     {
15626         this.store.on('load', this.onIOSViewLoad, this);
15627         
15628         return;
15629     },
15630     
15631     onIOSViewLoad : function()
15632     {
15633         if(this.store.getCount() < 1){
15634             return;
15635         }
15636         
15637         this.clearIOSView();
15638         
15639         if(this.allowBlank) {
15640             
15641             var default_text = '-- SELECT --';
15642             
15643             if(this.placeholder.length){
15644                 default_text = this.placeholder;
15645             }
15646             
15647             if(this.emptyTitle.length){
15648                 default_text += ' - ' + this.emptyTitle + ' -';
15649             }
15650             
15651             var opt = this.inputEl().createChild({
15652                 tag: 'option',
15653                 value : 0,
15654                 html : default_text
15655             });
15656             
15657             var o = {};
15658             o[this.valueField] = 0;
15659             o[this.displayField] = default_text;
15660             
15661             this.ios_options.push({
15662                 data : o,
15663                 el : opt
15664             });
15665             
15666         }
15667         
15668         this.store.data.each(function(d, rowIndex){
15669             
15670             var html = '';
15671             
15672             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15673                 html = d.data[this.displayField];
15674             }
15675             
15676             var value = '';
15677             
15678             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15679                 value = d.data[this.valueField];
15680             }
15681             
15682             var option = {
15683                 tag: 'option',
15684                 value : value,
15685                 html : html
15686             };
15687             
15688             if(this.value == d.data[this.valueField]){
15689                 option['selected'] = true;
15690             }
15691             
15692             var opt = this.inputEl().createChild(option);
15693             
15694             this.ios_options.push({
15695                 data : d.data,
15696                 el : opt
15697             });
15698             
15699         }, this);
15700         
15701         this.inputEl().on('change', function(){
15702            this.fireEvent('select', this);
15703         }, this);
15704         
15705     },
15706     
15707     clearIOSView: function()
15708     {
15709         this.inputEl().dom.innerHTML = '';
15710         
15711         this.ios_options = [];
15712     },
15713     
15714     setIOSValue: function(v)
15715     {
15716         this.value = v;
15717         
15718         if(!this.ios_options){
15719             return;
15720         }
15721         
15722         Roo.each(this.ios_options, function(opts){
15723            
15724            opts.el.dom.removeAttribute('selected');
15725            
15726            if(opts.data[this.valueField] != v){
15727                return;
15728            }
15729            
15730            opts.el.dom.setAttribute('selected', true);
15731            
15732         }, this);
15733     }
15734
15735     /** 
15736     * @cfg {Boolean} grow 
15737     * @hide 
15738     */
15739     /** 
15740     * @cfg {Number} growMin 
15741     * @hide 
15742     */
15743     /** 
15744     * @cfg {Number} growMax 
15745     * @hide 
15746     */
15747     /**
15748      * @hide
15749      * @method autoSize
15750      */
15751 });
15752
15753 Roo.apply(Roo.bootstrap.ComboBox,  {
15754     
15755     header : {
15756         tag: 'div',
15757         cls: 'modal-header',
15758         cn: [
15759             {
15760                 tag: 'h4',
15761                 cls: 'modal-title'
15762             }
15763         ]
15764     },
15765     
15766     body : {
15767         tag: 'div',
15768         cls: 'modal-body',
15769         cn: [
15770             {
15771                 tag: 'ul',
15772                 cls: 'list-group'
15773             }
15774         ]
15775     },
15776     
15777     listItemRadio : {
15778         tag: 'li',
15779         cls: 'list-group-item',
15780         cn: [
15781             {
15782                 tag: 'span',
15783                 cls: 'roo-combobox-list-group-item-value'
15784             },
15785             {
15786                 tag: 'div',
15787                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15788                 cn: [
15789                     {
15790                         tag: 'input',
15791                         type: 'radio'
15792                     },
15793                     {
15794                         tag: 'label'
15795                     }
15796                 ]
15797             }
15798         ]
15799     },
15800     
15801     listItemCheckbox : {
15802         tag: 'li',
15803         cls: 'list-group-item',
15804         cn: [
15805             {
15806                 tag: 'span',
15807                 cls: 'roo-combobox-list-group-item-value'
15808             },
15809             {
15810                 tag: 'div',
15811                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15812                 cn: [
15813                     {
15814                         tag: 'input',
15815                         type: 'checkbox'
15816                     },
15817                     {
15818                         tag: 'label'
15819                     }
15820                 ]
15821             }
15822         ]
15823     },
15824     
15825     emptyResult : {
15826         tag: 'div',
15827         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15828     },
15829     
15830     footer : {
15831         tag: 'div',
15832         cls: 'modal-footer',
15833         cn: [
15834             {
15835                 tag: 'div',
15836                 cls: 'row',
15837                 cn: [
15838                     {
15839                         tag: 'div',
15840                         cls: 'col-xs-6 text-left',
15841                         cn: {
15842                             tag: 'button',
15843                             cls: 'btn btn-danger roo-touch-view-cancel',
15844                             html: 'Cancel'
15845                         }
15846                     },
15847                     {
15848                         tag: 'div',
15849                         cls: 'col-xs-6 text-right',
15850                         cn: {
15851                             tag: 'button',
15852                             cls: 'btn btn-success roo-touch-view-ok',
15853                             html: 'OK'
15854                         }
15855                     }
15856                 ]
15857             }
15858         ]
15859         
15860     }
15861 });
15862
15863 Roo.apply(Roo.bootstrap.ComboBox,  {
15864     
15865     touchViewTemplate : {
15866         tag: 'div',
15867         cls: 'modal fade roo-combobox-touch-view',
15868         cn: [
15869             {
15870                 tag: 'div',
15871                 cls: 'modal-dialog',
15872                 style : 'position:fixed', // we have to fix position....
15873                 cn: [
15874                     {
15875                         tag: 'div',
15876                         cls: 'modal-content',
15877                         cn: [
15878                             Roo.bootstrap.ComboBox.header,
15879                             Roo.bootstrap.ComboBox.body,
15880                             Roo.bootstrap.ComboBox.footer
15881                         ]
15882                     }
15883                 ]
15884             }
15885         ]
15886     }
15887 });/*
15888  * Based on:
15889  * Ext JS Library 1.1.1
15890  * Copyright(c) 2006-2007, Ext JS, LLC.
15891  *
15892  * Originally Released Under LGPL - original licence link has changed is not relivant.
15893  *
15894  * Fork - LGPL
15895  * <script type="text/javascript">
15896  */
15897
15898 /**
15899  * @class Roo.View
15900  * @extends Roo.util.Observable
15901  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15902  * This class also supports single and multi selection modes. <br>
15903  * Create a data model bound view:
15904  <pre><code>
15905  var store = new Roo.data.Store(...);
15906
15907  var view = new Roo.View({
15908     el : "my-element",
15909     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15910  
15911     singleSelect: true,
15912     selectedClass: "ydataview-selected",
15913     store: store
15914  });
15915
15916  // listen for node click?
15917  view.on("click", function(vw, index, node, e){
15918  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15919  });
15920
15921  // load XML data
15922  dataModel.load("foobar.xml");
15923  </code></pre>
15924  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15925  * <br><br>
15926  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15927  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15928  * 
15929  * Note: old style constructor is still suported (container, template, config)
15930  * 
15931  * @constructor
15932  * Create a new View
15933  * @param {Object} config The config object
15934  * 
15935  */
15936 Roo.View = function(config, depreciated_tpl, depreciated_config){
15937     
15938     this.parent = false;
15939     
15940     if (typeof(depreciated_tpl) == 'undefined') {
15941         // new way.. - universal constructor.
15942         Roo.apply(this, config);
15943         this.el  = Roo.get(this.el);
15944     } else {
15945         // old format..
15946         this.el  = Roo.get(config);
15947         this.tpl = depreciated_tpl;
15948         Roo.apply(this, depreciated_config);
15949     }
15950     this.wrapEl  = this.el.wrap().wrap();
15951     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15952     
15953     
15954     if(typeof(this.tpl) == "string"){
15955         this.tpl = new Roo.Template(this.tpl);
15956     } else {
15957         // support xtype ctors..
15958         this.tpl = new Roo.factory(this.tpl, Roo);
15959     }
15960     
15961     
15962     this.tpl.compile();
15963     
15964     /** @private */
15965     this.addEvents({
15966         /**
15967          * @event beforeclick
15968          * Fires before a click is processed. Returns false to cancel the default action.
15969          * @param {Roo.View} this
15970          * @param {Number} index The index of the target node
15971          * @param {HTMLElement} node The target node
15972          * @param {Roo.EventObject} e The raw event object
15973          */
15974             "beforeclick" : true,
15975         /**
15976          * @event click
15977          * Fires when a template node is clicked.
15978          * @param {Roo.View} this
15979          * @param {Number} index The index of the target node
15980          * @param {HTMLElement} node The target node
15981          * @param {Roo.EventObject} e The raw event object
15982          */
15983             "click" : true,
15984         /**
15985          * @event dblclick
15986          * Fires when a template node is double clicked.
15987          * @param {Roo.View} this
15988          * @param {Number} index The index of the target node
15989          * @param {HTMLElement} node The target node
15990          * @param {Roo.EventObject} e The raw event object
15991          */
15992             "dblclick" : true,
15993         /**
15994          * @event contextmenu
15995          * Fires when a template node is right clicked.
15996          * @param {Roo.View} this
15997          * @param {Number} index The index of the target node
15998          * @param {HTMLElement} node The target node
15999          * @param {Roo.EventObject} e The raw event object
16000          */
16001             "contextmenu" : true,
16002         /**
16003          * @event selectionchange
16004          * Fires when the selected nodes change.
16005          * @param {Roo.View} this
16006          * @param {Array} selections Array of the selected nodes
16007          */
16008             "selectionchange" : true,
16009     
16010         /**
16011          * @event beforeselect
16012          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16013          * @param {Roo.View} this
16014          * @param {HTMLElement} node The node to be selected
16015          * @param {Array} selections Array of currently selected nodes
16016          */
16017             "beforeselect" : true,
16018         /**
16019          * @event preparedata
16020          * Fires on every row to render, to allow you to change the data.
16021          * @param {Roo.View} this
16022          * @param {Object} data to be rendered (change this)
16023          */
16024           "preparedata" : true
16025           
16026           
16027         });
16028
16029
16030
16031     this.el.on({
16032         "click": this.onClick,
16033         "dblclick": this.onDblClick,
16034         "contextmenu": this.onContextMenu,
16035         scope:this
16036     });
16037
16038     this.selections = [];
16039     this.nodes = [];
16040     this.cmp = new Roo.CompositeElementLite([]);
16041     if(this.store){
16042         this.store = Roo.factory(this.store, Roo.data);
16043         this.setStore(this.store, true);
16044     }
16045     
16046     if ( this.footer && this.footer.xtype) {
16047            
16048          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16049         
16050         this.footer.dataSource = this.store;
16051         this.footer.container = fctr;
16052         this.footer = Roo.factory(this.footer, Roo);
16053         fctr.insertFirst(this.el);
16054         
16055         // this is a bit insane - as the paging toolbar seems to detach the el..
16056 //        dom.parentNode.parentNode.parentNode
16057          // they get detached?
16058     }
16059     
16060     
16061     Roo.View.superclass.constructor.call(this);
16062     
16063     
16064 };
16065
16066 Roo.extend(Roo.View, Roo.util.Observable, {
16067     
16068      /**
16069      * @cfg {Roo.data.Store} store Data store to load data from.
16070      */
16071     store : false,
16072     
16073     /**
16074      * @cfg {String|Roo.Element} el The container element.
16075      */
16076     el : '',
16077     
16078     /**
16079      * @cfg {String|Roo.Template} tpl The template used by this View 
16080      */
16081     tpl : false,
16082     /**
16083      * @cfg {String} dataName the named area of the template to use as the data area
16084      *                          Works with domtemplates roo-name="name"
16085      */
16086     dataName: false,
16087     /**
16088      * @cfg {String} selectedClass The css class to add to selected nodes
16089      */
16090     selectedClass : "x-view-selected",
16091      /**
16092      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16093      */
16094     emptyText : "",
16095     
16096     /**
16097      * @cfg {String} text to display on mask (default Loading)
16098      */
16099     mask : false,
16100     /**
16101      * @cfg {Boolean} multiSelect Allow multiple selection
16102      */
16103     multiSelect : false,
16104     /**
16105      * @cfg {Boolean} singleSelect Allow single selection
16106      */
16107     singleSelect:  false,
16108     
16109     /**
16110      * @cfg {Boolean} toggleSelect - selecting 
16111      */
16112     toggleSelect : false,
16113     
16114     /**
16115      * @cfg {Boolean} tickable - selecting 
16116      */
16117     tickable : false,
16118     
16119     /**
16120      * Returns the element this view is bound to.
16121      * @return {Roo.Element}
16122      */
16123     getEl : function(){
16124         return this.wrapEl;
16125     },
16126     
16127     
16128
16129     /**
16130      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16131      */
16132     refresh : function(){
16133         //Roo.log('refresh');
16134         var t = this.tpl;
16135         
16136         // if we are using something like 'domtemplate', then
16137         // the what gets used is:
16138         // t.applySubtemplate(NAME, data, wrapping data..)
16139         // the outer template then get' applied with
16140         //     the store 'extra data'
16141         // and the body get's added to the
16142         //      roo-name="data" node?
16143         //      <span class='roo-tpl-{name}'></span> ?????
16144         
16145         
16146         
16147         this.clearSelections();
16148         this.el.update("");
16149         var html = [];
16150         var records = this.store.getRange();
16151         if(records.length < 1) {
16152             
16153             // is this valid??  = should it render a template??
16154             
16155             this.el.update(this.emptyText);
16156             return;
16157         }
16158         var el = this.el;
16159         if (this.dataName) {
16160             this.el.update(t.apply(this.store.meta)); //????
16161             el = this.el.child('.roo-tpl-' + this.dataName);
16162         }
16163         
16164         for(var i = 0, len = records.length; i < len; i++){
16165             var data = this.prepareData(records[i].data, i, records[i]);
16166             this.fireEvent("preparedata", this, data, i, records[i]);
16167             
16168             var d = Roo.apply({}, data);
16169             
16170             if(this.tickable){
16171                 Roo.apply(d, {'roo-id' : Roo.id()});
16172                 
16173                 var _this = this;
16174             
16175                 Roo.each(this.parent.item, function(item){
16176                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16177                         return;
16178                     }
16179                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16180                 });
16181             }
16182             
16183             html[html.length] = Roo.util.Format.trim(
16184                 this.dataName ?
16185                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16186                     t.apply(d)
16187             );
16188         }
16189         
16190         
16191         
16192         el.update(html.join(""));
16193         this.nodes = el.dom.childNodes;
16194         this.updateIndexes(0);
16195     },
16196     
16197
16198     /**
16199      * Function to override to reformat the data that is sent to
16200      * the template for each node.
16201      * DEPRICATED - use the preparedata event handler.
16202      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16203      * a JSON object for an UpdateManager bound view).
16204      */
16205     prepareData : function(data, index, record)
16206     {
16207         this.fireEvent("preparedata", this, data, index, record);
16208         return data;
16209     },
16210
16211     onUpdate : function(ds, record){
16212         // Roo.log('on update');   
16213         this.clearSelections();
16214         var index = this.store.indexOf(record);
16215         var n = this.nodes[index];
16216         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16217         n.parentNode.removeChild(n);
16218         this.updateIndexes(index, index);
16219     },
16220
16221     
16222     
16223 // --------- FIXME     
16224     onAdd : function(ds, records, index)
16225     {
16226         //Roo.log(['on Add', ds, records, index] );        
16227         this.clearSelections();
16228         if(this.nodes.length == 0){
16229             this.refresh();
16230             return;
16231         }
16232         var n = this.nodes[index];
16233         for(var i = 0, len = records.length; i < len; i++){
16234             var d = this.prepareData(records[i].data, i, records[i]);
16235             if(n){
16236                 this.tpl.insertBefore(n, d);
16237             }else{
16238                 
16239                 this.tpl.append(this.el, d);
16240             }
16241         }
16242         this.updateIndexes(index);
16243     },
16244
16245     onRemove : function(ds, record, index){
16246        // Roo.log('onRemove');
16247         this.clearSelections();
16248         var el = this.dataName  ?
16249             this.el.child('.roo-tpl-' + this.dataName) :
16250             this.el; 
16251         
16252         el.dom.removeChild(this.nodes[index]);
16253         this.updateIndexes(index);
16254     },
16255
16256     /**
16257      * Refresh an individual node.
16258      * @param {Number} index
16259      */
16260     refreshNode : function(index){
16261         this.onUpdate(this.store, this.store.getAt(index));
16262     },
16263
16264     updateIndexes : function(startIndex, endIndex){
16265         var ns = this.nodes;
16266         startIndex = startIndex || 0;
16267         endIndex = endIndex || ns.length - 1;
16268         for(var i = startIndex; i <= endIndex; i++){
16269             ns[i].nodeIndex = i;
16270         }
16271     },
16272
16273     /**
16274      * Changes the data store this view uses and refresh the view.
16275      * @param {Store} store
16276      */
16277     setStore : function(store, initial){
16278         if(!initial && this.store){
16279             this.store.un("datachanged", this.refresh);
16280             this.store.un("add", this.onAdd);
16281             this.store.un("remove", this.onRemove);
16282             this.store.un("update", this.onUpdate);
16283             this.store.un("clear", this.refresh);
16284             this.store.un("beforeload", this.onBeforeLoad);
16285             this.store.un("load", this.onLoad);
16286             this.store.un("loadexception", this.onLoad);
16287         }
16288         if(store){
16289           
16290             store.on("datachanged", this.refresh, this);
16291             store.on("add", this.onAdd, this);
16292             store.on("remove", this.onRemove, this);
16293             store.on("update", this.onUpdate, this);
16294             store.on("clear", this.refresh, this);
16295             store.on("beforeload", this.onBeforeLoad, this);
16296             store.on("load", this.onLoad, this);
16297             store.on("loadexception", this.onLoad, this);
16298         }
16299         
16300         if(store){
16301             this.refresh();
16302         }
16303     },
16304     /**
16305      * onbeforeLoad - masks the loading area.
16306      *
16307      */
16308     onBeforeLoad : function(store,opts)
16309     {
16310          //Roo.log('onBeforeLoad');   
16311         if (!opts.add) {
16312             this.el.update("");
16313         }
16314         this.el.mask(this.mask ? this.mask : "Loading" ); 
16315     },
16316     onLoad : function ()
16317     {
16318         this.el.unmask();
16319     },
16320     
16321
16322     /**
16323      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16324      * @param {HTMLElement} node
16325      * @return {HTMLElement} The template node
16326      */
16327     findItemFromChild : function(node){
16328         var el = this.dataName  ?
16329             this.el.child('.roo-tpl-' + this.dataName,true) :
16330             this.el.dom; 
16331         
16332         if(!node || node.parentNode == el){
16333                     return node;
16334             }
16335             var p = node.parentNode;
16336             while(p && p != el){
16337             if(p.parentNode == el){
16338                 return p;
16339             }
16340             p = p.parentNode;
16341         }
16342             return null;
16343     },
16344
16345     /** @ignore */
16346     onClick : function(e){
16347         var item = this.findItemFromChild(e.getTarget());
16348         if(item){
16349             var index = this.indexOf(item);
16350             if(this.onItemClick(item, index, e) !== false){
16351                 this.fireEvent("click", this, index, item, e);
16352             }
16353         }else{
16354             this.clearSelections();
16355         }
16356     },
16357
16358     /** @ignore */
16359     onContextMenu : function(e){
16360         var item = this.findItemFromChild(e.getTarget());
16361         if(item){
16362             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16363         }
16364     },
16365
16366     /** @ignore */
16367     onDblClick : function(e){
16368         var item = this.findItemFromChild(e.getTarget());
16369         if(item){
16370             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16371         }
16372     },
16373
16374     onItemClick : function(item, index, e)
16375     {
16376         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16377             return false;
16378         }
16379         if (this.toggleSelect) {
16380             var m = this.isSelected(item) ? 'unselect' : 'select';
16381             //Roo.log(m);
16382             var _t = this;
16383             _t[m](item, true, false);
16384             return true;
16385         }
16386         if(this.multiSelect || this.singleSelect){
16387             if(this.multiSelect && e.shiftKey && this.lastSelection){
16388                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16389             }else{
16390                 this.select(item, this.multiSelect && e.ctrlKey);
16391                 this.lastSelection = item;
16392             }
16393             
16394             if(!this.tickable){
16395                 e.preventDefault();
16396             }
16397             
16398         }
16399         return true;
16400     },
16401
16402     /**
16403      * Get the number of selected nodes.
16404      * @return {Number}
16405      */
16406     getSelectionCount : function(){
16407         return this.selections.length;
16408     },
16409
16410     /**
16411      * Get the currently selected nodes.
16412      * @return {Array} An array of HTMLElements
16413      */
16414     getSelectedNodes : function(){
16415         return this.selections;
16416     },
16417
16418     /**
16419      * Get the indexes of the selected nodes.
16420      * @return {Array}
16421      */
16422     getSelectedIndexes : function(){
16423         var indexes = [], s = this.selections;
16424         for(var i = 0, len = s.length; i < len; i++){
16425             indexes.push(s[i].nodeIndex);
16426         }
16427         return indexes;
16428     },
16429
16430     /**
16431      * Clear all selections
16432      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16433      */
16434     clearSelections : function(suppressEvent){
16435         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16436             this.cmp.elements = this.selections;
16437             this.cmp.removeClass(this.selectedClass);
16438             this.selections = [];
16439             if(!suppressEvent){
16440                 this.fireEvent("selectionchange", this, this.selections);
16441             }
16442         }
16443     },
16444
16445     /**
16446      * Returns true if the passed node is selected
16447      * @param {HTMLElement/Number} node The node or node index
16448      * @return {Boolean}
16449      */
16450     isSelected : function(node){
16451         var s = this.selections;
16452         if(s.length < 1){
16453             return false;
16454         }
16455         node = this.getNode(node);
16456         return s.indexOf(node) !== -1;
16457     },
16458
16459     /**
16460      * Selects nodes.
16461      * @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
16462      * @param {Boolean} keepExisting (optional) true to keep existing selections
16463      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16464      */
16465     select : function(nodeInfo, keepExisting, suppressEvent){
16466         if(nodeInfo instanceof Array){
16467             if(!keepExisting){
16468                 this.clearSelections(true);
16469             }
16470             for(var i = 0, len = nodeInfo.length; i < len; i++){
16471                 this.select(nodeInfo[i], true, true);
16472             }
16473             return;
16474         } 
16475         var node = this.getNode(nodeInfo);
16476         if(!node || this.isSelected(node)){
16477             return; // already selected.
16478         }
16479         if(!keepExisting){
16480             this.clearSelections(true);
16481         }
16482         
16483         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16484             Roo.fly(node).addClass(this.selectedClass);
16485             this.selections.push(node);
16486             if(!suppressEvent){
16487                 this.fireEvent("selectionchange", this, this.selections);
16488             }
16489         }
16490         
16491         
16492     },
16493       /**
16494      * Unselects nodes.
16495      * @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
16496      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16497      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16498      */
16499     unselect : function(nodeInfo, keepExisting, suppressEvent)
16500     {
16501         if(nodeInfo instanceof Array){
16502             Roo.each(this.selections, function(s) {
16503                 this.unselect(s, nodeInfo);
16504             }, this);
16505             return;
16506         }
16507         var node = this.getNode(nodeInfo);
16508         if(!node || !this.isSelected(node)){
16509             //Roo.log("not selected");
16510             return; // not selected.
16511         }
16512         // fireevent???
16513         var ns = [];
16514         Roo.each(this.selections, function(s) {
16515             if (s == node ) {
16516                 Roo.fly(node).removeClass(this.selectedClass);
16517
16518                 return;
16519             }
16520             ns.push(s);
16521         },this);
16522         
16523         this.selections= ns;
16524         this.fireEvent("selectionchange", this, this.selections);
16525     },
16526
16527     /**
16528      * Gets a template node.
16529      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16530      * @return {HTMLElement} The node or null if it wasn't found
16531      */
16532     getNode : function(nodeInfo){
16533         if(typeof nodeInfo == "string"){
16534             return document.getElementById(nodeInfo);
16535         }else if(typeof nodeInfo == "number"){
16536             return this.nodes[nodeInfo];
16537         }
16538         return nodeInfo;
16539     },
16540
16541     /**
16542      * Gets a range template nodes.
16543      * @param {Number} startIndex
16544      * @param {Number} endIndex
16545      * @return {Array} An array of nodes
16546      */
16547     getNodes : function(start, end){
16548         var ns = this.nodes;
16549         start = start || 0;
16550         end = typeof end == "undefined" ? ns.length - 1 : end;
16551         var nodes = [];
16552         if(start <= end){
16553             for(var i = start; i <= end; i++){
16554                 nodes.push(ns[i]);
16555             }
16556         } else{
16557             for(var i = start; i >= end; i--){
16558                 nodes.push(ns[i]);
16559             }
16560         }
16561         return nodes;
16562     },
16563
16564     /**
16565      * Finds the index of the passed node
16566      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16567      * @return {Number} The index of the node or -1
16568      */
16569     indexOf : function(node){
16570         node = this.getNode(node);
16571         if(typeof node.nodeIndex == "number"){
16572             return node.nodeIndex;
16573         }
16574         var ns = this.nodes;
16575         for(var i = 0, len = ns.length; i < len; i++){
16576             if(ns[i] == node){
16577                 return i;
16578             }
16579         }
16580         return -1;
16581     }
16582 });
16583 /*
16584  * - LGPL
16585  *
16586  * based on jquery fullcalendar
16587  * 
16588  */
16589
16590 Roo.bootstrap = Roo.bootstrap || {};
16591 /**
16592  * @class Roo.bootstrap.Calendar
16593  * @extends Roo.bootstrap.Component
16594  * Bootstrap Calendar class
16595  * @cfg {Boolean} loadMask (true|false) default false
16596  * @cfg {Object} header generate the user specific header of the calendar, default false
16597
16598  * @constructor
16599  * Create a new Container
16600  * @param {Object} config The config object
16601  */
16602
16603
16604
16605 Roo.bootstrap.Calendar = function(config){
16606     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16607      this.addEvents({
16608         /**
16609              * @event select
16610              * Fires when a date is selected
16611              * @param {DatePicker} this
16612              * @param {Date} date The selected date
16613              */
16614         'select': true,
16615         /**
16616              * @event monthchange
16617              * Fires when the displayed month changes 
16618              * @param {DatePicker} this
16619              * @param {Date} date The selected month
16620              */
16621         'monthchange': true,
16622         /**
16623              * @event evententer
16624              * Fires when mouse over an event
16625              * @param {Calendar} this
16626              * @param {event} Event
16627              */
16628         'evententer': true,
16629         /**
16630              * @event eventleave
16631              * Fires when the mouse leaves an
16632              * @param {Calendar} this
16633              * @param {event}
16634              */
16635         'eventleave': true,
16636         /**
16637              * @event eventclick
16638              * Fires when the mouse click an
16639              * @param {Calendar} this
16640              * @param {event}
16641              */
16642         'eventclick': true
16643         
16644     });
16645
16646 };
16647
16648 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16649     
16650      /**
16651      * @cfg {Number} startDay
16652      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16653      */
16654     startDay : 0,
16655     
16656     loadMask : false,
16657     
16658     header : false,
16659       
16660     getAutoCreate : function(){
16661         
16662         
16663         var fc_button = function(name, corner, style, content ) {
16664             return Roo.apply({},{
16665                 tag : 'span',
16666                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16667                          (corner.length ?
16668                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16669                             ''
16670                         ),
16671                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16672                 unselectable: 'on'
16673             });
16674         };
16675         
16676         var header = {};
16677         
16678         if(!this.header){
16679             header = {
16680                 tag : 'table',
16681                 cls : 'fc-header',
16682                 style : 'width:100%',
16683                 cn : [
16684                     {
16685                         tag: 'tr',
16686                         cn : [
16687                             {
16688                                 tag : 'td',
16689                                 cls : 'fc-header-left',
16690                                 cn : [
16691                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16692                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16693                                     { tag: 'span', cls: 'fc-header-space' },
16694                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16695
16696
16697                                 ]
16698                             },
16699
16700                             {
16701                                 tag : 'td',
16702                                 cls : 'fc-header-center',
16703                                 cn : [
16704                                     {
16705                                         tag: 'span',
16706                                         cls: 'fc-header-title',
16707                                         cn : {
16708                                             tag: 'H2',
16709                                             html : 'month / year'
16710                                         }
16711                                     }
16712
16713                                 ]
16714                             },
16715                             {
16716                                 tag : 'td',
16717                                 cls : 'fc-header-right',
16718                                 cn : [
16719                               /*      fc_button('month', 'left', '', 'month' ),
16720                                     fc_button('week', '', '', 'week' ),
16721                                     fc_button('day', 'right', '', 'day' )
16722                                 */    
16723
16724                                 ]
16725                             }
16726
16727                         ]
16728                     }
16729                 ]
16730             };
16731         }
16732         
16733         header = this.header;
16734         
16735        
16736         var cal_heads = function() {
16737             var ret = [];
16738             // fixme - handle this.
16739             
16740             for (var i =0; i < Date.dayNames.length; i++) {
16741                 var d = Date.dayNames[i];
16742                 ret.push({
16743                     tag: 'th',
16744                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16745                     html : d.substring(0,3)
16746                 });
16747                 
16748             }
16749             ret[0].cls += ' fc-first';
16750             ret[6].cls += ' fc-last';
16751             return ret;
16752         };
16753         var cal_cell = function(n) {
16754             return  {
16755                 tag: 'td',
16756                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16757                 cn : [
16758                     {
16759                         cn : [
16760                             {
16761                                 cls: 'fc-day-number',
16762                                 html: 'D'
16763                             },
16764                             {
16765                                 cls: 'fc-day-content',
16766                              
16767                                 cn : [
16768                                      {
16769                                         style: 'position: relative;' // height: 17px;
16770                                     }
16771                                 ]
16772                             }
16773                             
16774                             
16775                         ]
16776                     }
16777                 ]
16778                 
16779             }
16780         };
16781         var cal_rows = function() {
16782             
16783             var ret = [];
16784             for (var r = 0; r < 6; r++) {
16785                 var row= {
16786                     tag : 'tr',
16787                     cls : 'fc-week',
16788                     cn : []
16789                 };
16790                 
16791                 for (var i =0; i < Date.dayNames.length; i++) {
16792                     var d = Date.dayNames[i];
16793                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16794
16795                 }
16796                 row.cn[0].cls+=' fc-first';
16797                 row.cn[0].cn[0].style = 'min-height:90px';
16798                 row.cn[6].cls+=' fc-last';
16799                 ret.push(row);
16800                 
16801             }
16802             ret[0].cls += ' fc-first';
16803             ret[4].cls += ' fc-prev-last';
16804             ret[5].cls += ' fc-last';
16805             return ret;
16806             
16807         };
16808         
16809         var cal_table = {
16810             tag: 'table',
16811             cls: 'fc-border-separate',
16812             style : 'width:100%',
16813             cellspacing  : 0,
16814             cn : [
16815                 { 
16816                     tag: 'thead',
16817                     cn : [
16818                         { 
16819                             tag: 'tr',
16820                             cls : 'fc-first fc-last',
16821                             cn : cal_heads()
16822                         }
16823                     ]
16824                 },
16825                 { 
16826                     tag: 'tbody',
16827                     cn : cal_rows()
16828                 }
16829                   
16830             ]
16831         };
16832          
16833          var cfg = {
16834             cls : 'fc fc-ltr',
16835             cn : [
16836                 header,
16837                 {
16838                     cls : 'fc-content',
16839                     style : "position: relative;",
16840                     cn : [
16841                         {
16842                             cls : 'fc-view fc-view-month fc-grid',
16843                             style : 'position: relative',
16844                             unselectable : 'on',
16845                             cn : [
16846                                 {
16847                                     cls : 'fc-event-container',
16848                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16849                                 },
16850                                 cal_table
16851                             ]
16852                         }
16853                     ]
16854     
16855                 }
16856            ] 
16857             
16858         };
16859         
16860          
16861         
16862         return cfg;
16863     },
16864     
16865     
16866     initEvents : function()
16867     {
16868         if(!this.store){
16869             throw "can not find store for calendar";
16870         }
16871         
16872         var mark = {
16873             tag: "div",
16874             cls:"x-dlg-mask",
16875             style: "text-align:center",
16876             cn: [
16877                 {
16878                     tag: "div",
16879                     style: "background-color:white;width:50%;margin:250 auto",
16880                     cn: [
16881                         {
16882                             tag: "img",
16883                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16884                         },
16885                         {
16886                             tag: "span",
16887                             html: "Loading"
16888                         }
16889                         
16890                     ]
16891                 }
16892             ]
16893         };
16894         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16895         
16896         var size = this.el.select('.fc-content', true).first().getSize();
16897         this.maskEl.setSize(size.width, size.height);
16898         this.maskEl.enableDisplayMode("block");
16899         if(!this.loadMask){
16900             this.maskEl.hide();
16901         }
16902         
16903         this.store = Roo.factory(this.store, Roo.data);
16904         this.store.on('load', this.onLoad, this);
16905         this.store.on('beforeload', this.onBeforeLoad, this);
16906         
16907         this.resize();
16908         
16909         this.cells = this.el.select('.fc-day',true);
16910         //Roo.log(this.cells);
16911         this.textNodes = this.el.query('.fc-day-number');
16912         this.cells.addClassOnOver('fc-state-hover');
16913         
16914         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16915         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16916         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16917         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16918         
16919         this.on('monthchange', this.onMonthChange, this);
16920         
16921         this.update(new Date().clearTime());
16922     },
16923     
16924     resize : function() {
16925         var sz  = this.el.getSize();
16926         
16927         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16928         this.el.select('.fc-day-content div',true).setHeight(34);
16929     },
16930     
16931     
16932     // private
16933     showPrevMonth : function(e){
16934         this.update(this.activeDate.add("mo", -1));
16935     },
16936     showToday : function(e){
16937         this.update(new Date().clearTime());
16938     },
16939     // private
16940     showNextMonth : function(e){
16941         this.update(this.activeDate.add("mo", 1));
16942     },
16943
16944     // private
16945     showPrevYear : function(){
16946         this.update(this.activeDate.add("y", -1));
16947     },
16948
16949     // private
16950     showNextYear : function(){
16951         this.update(this.activeDate.add("y", 1));
16952     },
16953
16954     
16955    // private
16956     update : function(date)
16957     {
16958         var vd = this.activeDate;
16959         this.activeDate = date;
16960 //        if(vd && this.el){
16961 //            var t = date.getTime();
16962 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16963 //                Roo.log('using add remove');
16964 //                
16965 //                this.fireEvent('monthchange', this, date);
16966 //                
16967 //                this.cells.removeClass("fc-state-highlight");
16968 //                this.cells.each(function(c){
16969 //                   if(c.dateValue == t){
16970 //                       c.addClass("fc-state-highlight");
16971 //                       setTimeout(function(){
16972 //                            try{c.dom.firstChild.focus();}catch(e){}
16973 //                       }, 50);
16974 //                       return false;
16975 //                   }
16976 //                   return true;
16977 //                });
16978 //                return;
16979 //            }
16980 //        }
16981         
16982         var days = date.getDaysInMonth();
16983         
16984         var firstOfMonth = date.getFirstDateOfMonth();
16985         var startingPos = firstOfMonth.getDay()-this.startDay;
16986         
16987         if(startingPos < this.startDay){
16988             startingPos += 7;
16989         }
16990         
16991         var pm = date.add(Date.MONTH, -1);
16992         var prevStart = pm.getDaysInMonth()-startingPos;
16993 //        
16994         this.cells = this.el.select('.fc-day',true);
16995         this.textNodes = this.el.query('.fc-day-number');
16996         this.cells.addClassOnOver('fc-state-hover');
16997         
16998         var cells = this.cells.elements;
16999         var textEls = this.textNodes;
17000         
17001         Roo.each(cells, function(cell){
17002             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17003         });
17004         
17005         days += startingPos;
17006
17007         // convert everything to numbers so it's fast
17008         var day = 86400000;
17009         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17010         //Roo.log(d);
17011         //Roo.log(pm);
17012         //Roo.log(prevStart);
17013         
17014         var today = new Date().clearTime().getTime();
17015         var sel = date.clearTime().getTime();
17016         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17017         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17018         var ddMatch = this.disabledDatesRE;
17019         var ddText = this.disabledDatesText;
17020         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17021         var ddaysText = this.disabledDaysText;
17022         var format = this.format;
17023         
17024         var setCellClass = function(cal, cell){
17025             cell.row = 0;
17026             cell.events = [];
17027             cell.more = [];
17028             //Roo.log('set Cell Class');
17029             cell.title = "";
17030             var t = d.getTime();
17031             
17032             //Roo.log(d);
17033             
17034             cell.dateValue = t;
17035             if(t == today){
17036                 cell.className += " fc-today";
17037                 cell.className += " fc-state-highlight";
17038                 cell.title = cal.todayText;
17039             }
17040             if(t == sel){
17041                 // disable highlight in other month..
17042                 //cell.className += " fc-state-highlight";
17043                 
17044             }
17045             // disabling
17046             if(t < min) {
17047                 cell.className = " fc-state-disabled";
17048                 cell.title = cal.minText;
17049                 return;
17050             }
17051             if(t > max) {
17052                 cell.className = " fc-state-disabled";
17053                 cell.title = cal.maxText;
17054                 return;
17055             }
17056             if(ddays){
17057                 if(ddays.indexOf(d.getDay()) != -1){
17058                     cell.title = ddaysText;
17059                     cell.className = " fc-state-disabled";
17060                 }
17061             }
17062             if(ddMatch && format){
17063                 var fvalue = d.dateFormat(format);
17064                 if(ddMatch.test(fvalue)){
17065                     cell.title = ddText.replace("%0", fvalue);
17066                     cell.className = " fc-state-disabled";
17067                 }
17068             }
17069             
17070             if (!cell.initialClassName) {
17071                 cell.initialClassName = cell.dom.className;
17072             }
17073             
17074             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17075         };
17076
17077         var i = 0;
17078         
17079         for(; i < startingPos; i++) {
17080             textEls[i].innerHTML = (++prevStart);
17081             d.setDate(d.getDate()+1);
17082             
17083             cells[i].className = "fc-past fc-other-month";
17084             setCellClass(this, cells[i]);
17085         }
17086         
17087         var intDay = 0;
17088         
17089         for(; i < days; i++){
17090             intDay = i - startingPos + 1;
17091             textEls[i].innerHTML = (intDay);
17092             d.setDate(d.getDate()+1);
17093             
17094             cells[i].className = ''; // "x-date-active";
17095             setCellClass(this, cells[i]);
17096         }
17097         var extraDays = 0;
17098         
17099         for(; i < 42; i++) {
17100             textEls[i].innerHTML = (++extraDays);
17101             d.setDate(d.getDate()+1);
17102             
17103             cells[i].className = "fc-future fc-other-month";
17104             setCellClass(this, cells[i]);
17105         }
17106         
17107         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17108         
17109         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17110         
17111         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17112         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17113         
17114         if(totalRows != 6){
17115             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17116             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17117         }
17118         
17119         this.fireEvent('monthchange', this, date);
17120         
17121         
17122         /*
17123         if(!this.internalRender){
17124             var main = this.el.dom.firstChild;
17125             var w = main.offsetWidth;
17126             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17127             Roo.fly(main).setWidth(w);
17128             this.internalRender = true;
17129             // opera does not respect the auto grow header center column
17130             // then, after it gets a width opera refuses to recalculate
17131             // without a second pass
17132             if(Roo.isOpera && !this.secondPass){
17133                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17134                 this.secondPass = true;
17135                 this.update.defer(10, this, [date]);
17136             }
17137         }
17138         */
17139         
17140     },
17141     
17142     findCell : function(dt) {
17143         dt = dt.clearTime().getTime();
17144         var ret = false;
17145         this.cells.each(function(c){
17146             //Roo.log("check " +c.dateValue + '?=' + dt);
17147             if(c.dateValue == dt){
17148                 ret = c;
17149                 return false;
17150             }
17151             return true;
17152         });
17153         
17154         return ret;
17155     },
17156     
17157     findCells : function(ev) {
17158         var s = ev.start.clone().clearTime().getTime();
17159        // Roo.log(s);
17160         var e= ev.end.clone().clearTime().getTime();
17161        // Roo.log(e);
17162         var ret = [];
17163         this.cells.each(function(c){
17164              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17165             
17166             if(c.dateValue > e){
17167                 return ;
17168             }
17169             if(c.dateValue < s){
17170                 return ;
17171             }
17172             ret.push(c);
17173         });
17174         
17175         return ret;    
17176     },
17177     
17178 //    findBestRow: function(cells)
17179 //    {
17180 //        var ret = 0;
17181 //        
17182 //        for (var i =0 ; i < cells.length;i++) {
17183 //            ret  = Math.max(cells[i].rows || 0,ret);
17184 //        }
17185 //        return ret;
17186 //        
17187 //    },
17188     
17189     
17190     addItem : function(ev)
17191     {
17192         // look for vertical location slot in
17193         var cells = this.findCells(ev);
17194         
17195 //        ev.row = this.findBestRow(cells);
17196         
17197         // work out the location.
17198         
17199         var crow = false;
17200         var rows = [];
17201         for(var i =0; i < cells.length; i++) {
17202             
17203             cells[i].row = cells[0].row;
17204             
17205             if(i == 0){
17206                 cells[i].row = cells[i].row + 1;
17207             }
17208             
17209             if (!crow) {
17210                 crow = {
17211                     start : cells[i],
17212                     end :  cells[i]
17213                 };
17214                 continue;
17215             }
17216             if (crow.start.getY() == cells[i].getY()) {
17217                 // on same row.
17218                 crow.end = cells[i];
17219                 continue;
17220             }
17221             // different row.
17222             rows.push(crow);
17223             crow = {
17224                 start: cells[i],
17225                 end : cells[i]
17226             };
17227             
17228         }
17229         
17230         rows.push(crow);
17231         ev.els = [];
17232         ev.rows = rows;
17233         ev.cells = cells;
17234         
17235         cells[0].events.push(ev);
17236         
17237         this.calevents.push(ev);
17238     },
17239     
17240     clearEvents: function() {
17241         
17242         if(!this.calevents){
17243             return;
17244         }
17245         
17246         Roo.each(this.cells.elements, function(c){
17247             c.row = 0;
17248             c.events = [];
17249             c.more = [];
17250         });
17251         
17252         Roo.each(this.calevents, function(e) {
17253             Roo.each(e.els, function(el) {
17254                 el.un('mouseenter' ,this.onEventEnter, this);
17255                 el.un('mouseleave' ,this.onEventLeave, this);
17256                 el.remove();
17257             },this);
17258         },this);
17259         
17260         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17261             e.remove();
17262         });
17263         
17264     },
17265     
17266     renderEvents: function()
17267     {   
17268         var _this = this;
17269         
17270         this.cells.each(function(c) {
17271             
17272             if(c.row < 5){
17273                 return;
17274             }
17275             
17276             var ev = c.events;
17277             
17278             var r = 4;
17279             if(c.row != c.events.length){
17280                 r = 4 - (4 - (c.row - c.events.length));
17281             }
17282             
17283             c.events = ev.slice(0, r);
17284             c.more = ev.slice(r);
17285             
17286             if(c.more.length && c.more.length == 1){
17287                 c.events.push(c.more.pop());
17288             }
17289             
17290             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17291             
17292         });
17293             
17294         this.cells.each(function(c) {
17295             
17296             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17297             
17298             
17299             for (var e = 0; e < c.events.length; e++){
17300                 var ev = c.events[e];
17301                 var rows = ev.rows;
17302                 
17303                 for(var i = 0; i < rows.length; i++) {
17304                 
17305                     // how many rows should it span..
17306
17307                     var  cfg = {
17308                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17309                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17310
17311                         unselectable : "on",
17312                         cn : [
17313                             {
17314                                 cls: 'fc-event-inner',
17315                                 cn : [
17316     //                                {
17317     //                                  tag:'span',
17318     //                                  cls: 'fc-event-time',
17319     //                                  html : cells.length > 1 ? '' : ev.time
17320     //                                },
17321                                     {
17322                                       tag:'span',
17323                                       cls: 'fc-event-title',
17324                                       html : String.format('{0}', ev.title)
17325                                     }
17326
17327
17328                                 ]
17329                             },
17330                             {
17331                                 cls: 'ui-resizable-handle ui-resizable-e',
17332                                 html : '&nbsp;&nbsp;&nbsp'
17333                             }
17334
17335                         ]
17336                     };
17337
17338                     if (i == 0) {
17339                         cfg.cls += ' fc-event-start';
17340                     }
17341                     if ((i+1) == rows.length) {
17342                         cfg.cls += ' fc-event-end';
17343                     }
17344
17345                     var ctr = _this.el.select('.fc-event-container',true).first();
17346                     var cg = ctr.createChild(cfg);
17347
17348                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17349                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17350
17351                     var r = (c.more.length) ? 1 : 0;
17352                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17353                     cg.setWidth(ebox.right - sbox.x -2);
17354
17355                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17356                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17357                     cg.on('click', _this.onEventClick, _this, ev);
17358
17359                     ev.els.push(cg);
17360                     
17361                 }
17362                 
17363             }
17364             
17365             
17366             if(c.more.length){
17367                 var  cfg = {
17368                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17369                     style : 'position: absolute',
17370                     unselectable : "on",
17371                     cn : [
17372                         {
17373                             cls: 'fc-event-inner',
17374                             cn : [
17375                                 {
17376                                   tag:'span',
17377                                   cls: 'fc-event-title',
17378                                   html : 'More'
17379                                 }
17380
17381
17382                             ]
17383                         },
17384                         {
17385                             cls: 'ui-resizable-handle ui-resizable-e',
17386                             html : '&nbsp;&nbsp;&nbsp'
17387                         }
17388
17389                     ]
17390                 };
17391
17392                 var ctr = _this.el.select('.fc-event-container',true).first();
17393                 var cg = ctr.createChild(cfg);
17394
17395                 var sbox = c.select('.fc-day-content',true).first().getBox();
17396                 var ebox = c.select('.fc-day-content',true).first().getBox();
17397                 //Roo.log(cg);
17398                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17399                 cg.setWidth(ebox.right - sbox.x -2);
17400
17401                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17402                 
17403             }
17404             
17405         });
17406         
17407         
17408         
17409     },
17410     
17411     onEventEnter: function (e, el,event,d) {
17412         this.fireEvent('evententer', this, el, event);
17413     },
17414     
17415     onEventLeave: function (e, el,event,d) {
17416         this.fireEvent('eventleave', this, el, event);
17417     },
17418     
17419     onEventClick: function (e, el,event,d) {
17420         this.fireEvent('eventclick', this, el, event);
17421     },
17422     
17423     onMonthChange: function () {
17424         this.store.load();
17425     },
17426     
17427     onMoreEventClick: function(e, el, more)
17428     {
17429         var _this = this;
17430         
17431         this.calpopover.placement = 'right';
17432         this.calpopover.setTitle('More');
17433         
17434         this.calpopover.setContent('');
17435         
17436         var ctr = this.calpopover.el.select('.popover-content', true).first();
17437         
17438         Roo.each(more, function(m){
17439             var cfg = {
17440                 cls : 'fc-event-hori fc-event-draggable',
17441                 html : m.title
17442             };
17443             var cg = ctr.createChild(cfg);
17444             
17445             cg.on('click', _this.onEventClick, _this, m);
17446         });
17447         
17448         this.calpopover.show(el);
17449         
17450         
17451     },
17452     
17453     onLoad: function () 
17454     {   
17455         this.calevents = [];
17456         var cal = this;
17457         
17458         if(this.store.getCount() > 0){
17459             this.store.data.each(function(d){
17460                cal.addItem({
17461                     id : d.data.id,
17462                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17463                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17464                     time : d.data.start_time,
17465                     title : d.data.title,
17466                     description : d.data.description,
17467                     venue : d.data.venue
17468                 });
17469             });
17470         }
17471         
17472         this.renderEvents();
17473         
17474         if(this.calevents.length && this.loadMask){
17475             this.maskEl.hide();
17476         }
17477     },
17478     
17479     onBeforeLoad: function()
17480     {
17481         this.clearEvents();
17482         if(this.loadMask){
17483             this.maskEl.show();
17484         }
17485     }
17486 });
17487
17488  
17489  /*
17490  * - LGPL
17491  *
17492  * element
17493  * 
17494  */
17495
17496 /**
17497  * @class Roo.bootstrap.Popover
17498  * @extends Roo.bootstrap.Component
17499  * Bootstrap Popover class
17500  * @cfg {String} html contents of the popover   (or false to use children..)
17501  * @cfg {String} title of popover (or false to hide)
17502  * @cfg {String} placement how it is placed
17503  * @cfg {String} trigger click || hover (or false to trigger manually)
17504  * @cfg {String} over what (parent or false to trigger manually.)
17505  * @cfg {Number} delay - delay before showing
17506  
17507  * @constructor
17508  * Create a new Popover
17509  * @param {Object} config The config object
17510  */
17511
17512 Roo.bootstrap.Popover = function(config){
17513     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17514     
17515     this.addEvents({
17516         // raw events
17517          /**
17518          * @event show
17519          * After the popover show
17520          * 
17521          * @param {Roo.bootstrap.Popover} this
17522          */
17523         "show" : true,
17524         /**
17525          * @event hide
17526          * After the popover hide
17527          * 
17528          * @param {Roo.bootstrap.Popover} this
17529          */
17530         "hide" : true
17531     });
17532 };
17533
17534 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17535     
17536     title: 'Fill in a title',
17537     html: false,
17538     
17539     placement : 'right',
17540     trigger : 'hover', // hover
17541     
17542     delay : 0,
17543     
17544     over: 'parent',
17545     
17546     can_build_overlaid : false,
17547     
17548     getChildContainer : function()
17549     {
17550         return this.el.select('.popover-content',true).first();
17551     },
17552     
17553     getAutoCreate : function(){
17554          
17555         var cfg = {
17556            cls : 'popover roo-dynamic',
17557            style: 'display:block',
17558            cn : [
17559                 {
17560                     cls : 'arrow'
17561                 },
17562                 {
17563                     cls : 'popover-inner',
17564                     cn : [
17565                         {
17566                             tag: 'h3',
17567                             cls: 'popover-title',
17568                             html : this.title
17569                         },
17570                         {
17571                             cls : 'popover-content',
17572                             html : this.html
17573                         }
17574                     ]
17575                     
17576                 }
17577            ]
17578         };
17579         
17580         return cfg;
17581     },
17582     setTitle: function(str)
17583     {
17584         this.title = str;
17585         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17586     },
17587     setContent: function(str)
17588     {
17589         this.html = str;
17590         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17591     },
17592     // as it get's added to the bottom of the page.
17593     onRender : function(ct, position)
17594     {
17595         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17596         if(!this.el){
17597             var cfg = Roo.apply({},  this.getAutoCreate());
17598             cfg.id = Roo.id();
17599             
17600             if (this.cls) {
17601                 cfg.cls += ' ' + this.cls;
17602             }
17603             if (this.style) {
17604                 cfg.style = this.style;
17605             }
17606             //Roo.log("adding to ");
17607             this.el = Roo.get(document.body).createChild(cfg, position);
17608 //            Roo.log(this.el);
17609         }
17610         this.initEvents();
17611     },
17612     
17613     initEvents : function()
17614     {
17615         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17616         this.el.enableDisplayMode('block');
17617         this.el.hide();
17618         if (this.over === false) {
17619             return; 
17620         }
17621         if (this.triggers === false) {
17622             return;
17623         }
17624         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17625         var triggers = this.trigger ? this.trigger.split(' ') : [];
17626         Roo.each(triggers, function(trigger) {
17627         
17628             if (trigger == 'click') {
17629                 on_el.on('click', this.toggle, this);
17630             } else if (trigger != 'manual') {
17631                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17632                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17633       
17634                 on_el.on(eventIn  ,this.enter, this);
17635                 on_el.on(eventOut, this.leave, this);
17636             }
17637         }, this);
17638         
17639     },
17640     
17641     
17642     // private
17643     timeout : null,
17644     hoverState : null,
17645     
17646     toggle : function () {
17647         this.hoverState == 'in' ? this.leave() : this.enter();
17648     },
17649     
17650     enter : function () {
17651         
17652         clearTimeout(this.timeout);
17653     
17654         this.hoverState = 'in';
17655     
17656         if (!this.delay || !this.delay.show) {
17657             this.show();
17658             return;
17659         }
17660         var _t = this;
17661         this.timeout = setTimeout(function () {
17662             if (_t.hoverState == 'in') {
17663                 _t.show();
17664             }
17665         }, this.delay.show)
17666     },
17667     
17668     leave : function() {
17669         clearTimeout(this.timeout);
17670     
17671         this.hoverState = 'out';
17672     
17673         if (!this.delay || !this.delay.hide) {
17674             this.hide();
17675             return;
17676         }
17677         var _t = this;
17678         this.timeout = setTimeout(function () {
17679             if (_t.hoverState == 'out') {
17680                 _t.hide();
17681             }
17682         }, this.delay.hide)
17683     },
17684     
17685     show : function (on_el)
17686     {
17687         if (!on_el) {
17688             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17689         }
17690         
17691         // set content.
17692         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17693         if (this.html !== false) {
17694             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17695         }
17696         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17697         if (!this.title.length) {
17698             this.el.select('.popover-title',true).hide();
17699         }
17700         
17701         var placement = typeof this.placement == 'function' ?
17702             this.placement.call(this, this.el, on_el) :
17703             this.placement;
17704             
17705         var autoToken = /\s?auto?\s?/i;
17706         var autoPlace = autoToken.test(placement);
17707         if (autoPlace) {
17708             placement = placement.replace(autoToken, '') || 'top';
17709         }
17710         
17711         //this.el.detach()
17712         //this.el.setXY([0,0]);
17713         this.el.show();
17714         this.el.dom.style.display='block';
17715         this.el.addClass(placement);
17716         
17717         //this.el.appendTo(on_el);
17718         
17719         var p = this.getPosition();
17720         var box = this.el.getBox();
17721         
17722         if (autoPlace) {
17723             // fixme..
17724         }
17725         var align = Roo.bootstrap.Popover.alignment[placement];
17726         
17727 //        Roo.log(align);
17728         this.el.alignTo(on_el, align[0],align[1]);
17729         //var arrow = this.el.select('.arrow',true).first();
17730         //arrow.set(align[2], 
17731         
17732         this.el.addClass('in');
17733         
17734         
17735         if (this.el.hasClass('fade')) {
17736             // fade it?
17737         }
17738         
17739         this.hoverState = 'in';
17740         
17741         this.fireEvent('show', this);
17742         
17743     },
17744     hide : function()
17745     {
17746         this.el.setXY([0,0]);
17747         this.el.removeClass('in');
17748         this.el.hide();
17749         this.hoverState = null;
17750         
17751         this.fireEvent('hide', this);
17752     }
17753     
17754 });
17755
17756 Roo.bootstrap.Popover.alignment = {
17757     'left' : ['r-l', [-10,0], 'right'],
17758     'right' : ['l-r', [10,0], 'left'],
17759     'bottom' : ['t-b', [0,10], 'top'],
17760     'top' : [ 'b-t', [0,-10], 'bottom']
17761 };
17762
17763  /*
17764  * - LGPL
17765  *
17766  * Progress
17767  * 
17768  */
17769
17770 /**
17771  * @class Roo.bootstrap.Progress
17772  * @extends Roo.bootstrap.Component
17773  * Bootstrap Progress class
17774  * @cfg {Boolean} striped striped of the progress bar
17775  * @cfg {Boolean} active animated of the progress bar
17776  * 
17777  * 
17778  * @constructor
17779  * Create a new Progress
17780  * @param {Object} config The config object
17781  */
17782
17783 Roo.bootstrap.Progress = function(config){
17784     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17785 };
17786
17787 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17788     
17789     striped : false,
17790     active: false,
17791     
17792     getAutoCreate : function(){
17793         var cfg = {
17794             tag: 'div',
17795             cls: 'progress'
17796         };
17797         
17798         
17799         if(this.striped){
17800             cfg.cls += ' progress-striped';
17801         }
17802       
17803         if(this.active){
17804             cfg.cls += ' active';
17805         }
17806         
17807         
17808         return cfg;
17809     }
17810    
17811 });
17812
17813  
17814
17815  /*
17816  * - LGPL
17817  *
17818  * ProgressBar
17819  * 
17820  */
17821
17822 /**
17823  * @class Roo.bootstrap.ProgressBar
17824  * @extends Roo.bootstrap.Component
17825  * Bootstrap ProgressBar class
17826  * @cfg {Number} aria_valuenow aria-value now
17827  * @cfg {Number} aria_valuemin aria-value min
17828  * @cfg {Number} aria_valuemax aria-value max
17829  * @cfg {String} label label for the progress bar
17830  * @cfg {String} panel (success | info | warning | danger )
17831  * @cfg {String} role role of the progress bar
17832  * @cfg {String} sr_only text
17833  * 
17834  * 
17835  * @constructor
17836  * Create a new ProgressBar
17837  * @param {Object} config The config object
17838  */
17839
17840 Roo.bootstrap.ProgressBar = function(config){
17841     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17842 };
17843
17844 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17845     
17846     aria_valuenow : 0,
17847     aria_valuemin : 0,
17848     aria_valuemax : 100,
17849     label : false,
17850     panel : false,
17851     role : false,
17852     sr_only: false,
17853     
17854     getAutoCreate : function()
17855     {
17856         
17857         var cfg = {
17858             tag: 'div',
17859             cls: 'progress-bar',
17860             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17861         };
17862         
17863         if(this.sr_only){
17864             cfg.cn = {
17865                 tag: 'span',
17866                 cls: 'sr-only',
17867                 html: this.sr_only
17868             }
17869         }
17870         
17871         if(this.role){
17872             cfg.role = this.role;
17873         }
17874         
17875         if(this.aria_valuenow){
17876             cfg['aria-valuenow'] = this.aria_valuenow;
17877         }
17878         
17879         if(this.aria_valuemin){
17880             cfg['aria-valuemin'] = this.aria_valuemin;
17881         }
17882         
17883         if(this.aria_valuemax){
17884             cfg['aria-valuemax'] = this.aria_valuemax;
17885         }
17886         
17887         if(this.label && !this.sr_only){
17888             cfg.html = this.label;
17889         }
17890         
17891         if(this.panel){
17892             cfg.cls += ' progress-bar-' + this.panel;
17893         }
17894         
17895         return cfg;
17896     },
17897     
17898     update : function(aria_valuenow)
17899     {
17900         this.aria_valuenow = aria_valuenow;
17901         
17902         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17903     }
17904    
17905 });
17906
17907  
17908
17909  /*
17910  * - LGPL
17911  *
17912  * column
17913  * 
17914  */
17915
17916 /**
17917  * @class Roo.bootstrap.TabGroup
17918  * @extends Roo.bootstrap.Column
17919  * Bootstrap Column class
17920  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17921  * @cfg {Boolean} carousel true to make the group behave like a carousel
17922  * @cfg {Boolean} bullets show bullets for the panels
17923  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17924  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17925  * @cfg {Boolean} showarrow (true|false) show arrow default true
17926  * 
17927  * @constructor
17928  * Create a new TabGroup
17929  * @param {Object} config The config object
17930  */
17931
17932 Roo.bootstrap.TabGroup = function(config){
17933     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17934     if (!this.navId) {
17935         this.navId = Roo.id();
17936     }
17937     this.tabs = [];
17938     Roo.bootstrap.TabGroup.register(this);
17939     
17940 };
17941
17942 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17943     
17944     carousel : false,
17945     transition : false,
17946     bullets : 0,
17947     timer : 0,
17948     autoslide : false,
17949     slideFn : false,
17950     slideOnTouch : false,
17951     showarrow : true,
17952     
17953     getAutoCreate : function()
17954     {
17955         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17956         
17957         cfg.cls += ' tab-content';
17958         
17959         if (this.carousel) {
17960             cfg.cls += ' carousel slide';
17961             
17962             cfg.cn = [{
17963                cls : 'carousel-inner',
17964                cn : []
17965             }];
17966         
17967             if(this.bullets  && !Roo.isTouch){
17968                 
17969                 var bullets = {
17970                     cls : 'carousel-bullets',
17971                     cn : []
17972                 };
17973                
17974                 if(this.bullets_cls){
17975                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17976                 }
17977                 
17978                 bullets.cn.push({
17979                     cls : 'clear'
17980                 });
17981                 
17982                 cfg.cn[0].cn.push(bullets);
17983             }
17984             
17985             if(this.showarrow){
17986                 cfg.cn[0].cn.push({
17987                     tag : 'div',
17988                     class : 'carousel-arrow',
17989                     cn : [
17990                         {
17991                             tag : 'div',
17992                             class : 'carousel-prev',
17993                             cn : [
17994                                 {
17995                                     tag : 'i',
17996                                     class : 'fa fa-chevron-left'
17997                                 }
17998                             ]
17999                         },
18000                         {
18001                             tag : 'div',
18002                             class : 'carousel-next',
18003                             cn : [
18004                                 {
18005                                     tag : 'i',
18006                                     class : 'fa fa-chevron-right'
18007                                 }
18008                             ]
18009                         }
18010                     ]
18011                 });
18012             }
18013             
18014         }
18015         
18016         return cfg;
18017     },
18018     
18019     initEvents:  function()
18020     {
18021 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18022 //            this.el.on("touchstart", this.onTouchStart, this);
18023 //        }
18024         
18025         if(this.autoslide){
18026             var _this = this;
18027             
18028             this.slideFn = window.setInterval(function() {
18029                 _this.showPanelNext();
18030             }, this.timer);
18031         }
18032         
18033         if(this.showarrow){
18034             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18035             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18036         }
18037         
18038         
18039     },
18040     
18041 //    onTouchStart : function(e, el, o)
18042 //    {
18043 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18044 //            return;
18045 //        }
18046 //        
18047 //        this.showPanelNext();
18048 //    },
18049     
18050     
18051     getChildContainer : function()
18052     {
18053         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18054     },
18055     
18056     /**
18057     * register a Navigation item
18058     * @param {Roo.bootstrap.NavItem} the navitem to add
18059     */
18060     register : function(item)
18061     {
18062         this.tabs.push( item);
18063         item.navId = this.navId; // not really needed..
18064         this.addBullet();
18065     
18066     },
18067     
18068     getActivePanel : function()
18069     {
18070         var r = false;
18071         Roo.each(this.tabs, function(t) {
18072             if (t.active) {
18073                 r = t;
18074                 return false;
18075             }
18076             return null;
18077         });
18078         return r;
18079         
18080     },
18081     getPanelByName : function(n)
18082     {
18083         var r = false;
18084         Roo.each(this.tabs, function(t) {
18085             if (t.tabId == n) {
18086                 r = t;
18087                 return false;
18088             }
18089             return null;
18090         });
18091         return r;
18092     },
18093     indexOfPanel : function(p)
18094     {
18095         var r = false;
18096         Roo.each(this.tabs, function(t,i) {
18097             if (t.tabId == p.tabId) {
18098                 r = i;
18099                 return false;
18100             }
18101             return null;
18102         });
18103         return r;
18104     },
18105     /**
18106      * show a specific panel
18107      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18108      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18109      */
18110     showPanel : function (pan)
18111     {
18112         if(this.transition || typeof(pan) == 'undefined'){
18113             Roo.log("waiting for the transitionend");
18114             return;
18115         }
18116         
18117         if (typeof(pan) == 'number') {
18118             pan = this.tabs[pan];
18119         }
18120         
18121         if (typeof(pan) == 'string') {
18122             pan = this.getPanelByName(pan);
18123         }
18124         
18125         var cur = this.getActivePanel();
18126         
18127         if(!pan || !cur){
18128             Roo.log('pan or acitve pan is undefined');
18129             return false;
18130         }
18131         
18132         if (pan.tabId == this.getActivePanel().tabId) {
18133             return true;
18134         }
18135         
18136         if (false === cur.fireEvent('beforedeactivate')) {
18137             return false;
18138         }
18139         
18140         if(this.bullets > 0 && !Roo.isTouch){
18141             this.setActiveBullet(this.indexOfPanel(pan));
18142         }
18143         
18144         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18145             
18146             this.transition = true;
18147             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18148             var lr = dir == 'next' ? 'left' : 'right';
18149             pan.el.addClass(dir); // or prev
18150             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18151             cur.el.addClass(lr); // or right
18152             pan.el.addClass(lr);
18153             
18154             var _this = this;
18155             cur.el.on('transitionend', function() {
18156                 Roo.log("trans end?");
18157                 
18158                 pan.el.removeClass([lr,dir]);
18159                 pan.setActive(true);
18160                 
18161                 cur.el.removeClass([lr]);
18162                 cur.setActive(false);
18163                 
18164                 _this.transition = false;
18165                 
18166             }, this, { single:  true } );
18167             
18168             return true;
18169         }
18170         
18171         cur.setActive(false);
18172         pan.setActive(true);
18173         
18174         return true;
18175         
18176     },
18177     showPanelNext : function()
18178     {
18179         var i = this.indexOfPanel(this.getActivePanel());
18180         
18181         if (i >= this.tabs.length - 1 && !this.autoslide) {
18182             return;
18183         }
18184         
18185         if (i >= this.tabs.length - 1 && this.autoslide) {
18186             i = -1;
18187         }
18188         
18189         this.showPanel(this.tabs[i+1]);
18190     },
18191     
18192     showPanelPrev : function()
18193     {
18194         var i = this.indexOfPanel(this.getActivePanel());
18195         
18196         if (i  < 1 && !this.autoslide) {
18197             return;
18198         }
18199         
18200         if (i < 1 && this.autoslide) {
18201             i = this.tabs.length;
18202         }
18203         
18204         this.showPanel(this.tabs[i-1]);
18205     },
18206     
18207     
18208     addBullet: function()
18209     {
18210         if(!this.bullets || Roo.isTouch){
18211             return;
18212         }
18213         var ctr = this.el.select('.carousel-bullets',true).first();
18214         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18215         var bullet = ctr.createChild({
18216             cls : 'bullet bullet-' + i
18217         },ctr.dom.lastChild);
18218         
18219         
18220         var _this = this;
18221         
18222         bullet.on('click', (function(e, el, o, ii, t){
18223
18224             e.preventDefault();
18225
18226             this.showPanel(ii);
18227
18228             if(this.autoslide && this.slideFn){
18229                 clearInterval(this.slideFn);
18230                 this.slideFn = window.setInterval(function() {
18231                     _this.showPanelNext();
18232                 }, this.timer);
18233             }
18234
18235         }).createDelegate(this, [i, bullet], true));
18236                 
18237         
18238     },
18239      
18240     setActiveBullet : function(i)
18241     {
18242         if(Roo.isTouch){
18243             return;
18244         }
18245         
18246         Roo.each(this.el.select('.bullet', true).elements, function(el){
18247             el.removeClass('selected');
18248         });
18249
18250         var bullet = this.el.select('.bullet-' + i, true).first();
18251         
18252         if(!bullet){
18253             return;
18254         }
18255         
18256         bullet.addClass('selected');
18257     }
18258     
18259     
18260   
18261 });
18262
18263  
18264
18265  
18266  
18267 Roo.apply(Roo.bootstrap.TabGroup, {
18268     
18269     groups: {},
18270      /**
18271     * register a Navigation Group
18272     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18273     */
18274     register : function(navgrp)
18275     {
18276         this.groups[navgrp.navId] = navgrp;
18277         
18278     },
18279     /**
18280     * fetch a Navigation Group based on the navigation ID
18281     * if one does not exist , it will get created.
18282     * @param {string} the navgroup to add
18283     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18284     */
18285     get: function(navId) {
18286         if (typeof(this.groups[navId]) == 'undefined') {
18287             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18288         }
18289         return this.groups[navId] ;
18290     }
18291     
18292     
18293     
18294 });
18295
18296  /*
18297  * - LGPL
18298  *
18299  * TabPanel
18300  * 
18301  */
18302
18303 /**
18304  * @class Roo.bootstrap.TabPanel
18305  * @extends Roo.bootstrap.Component
18306  * Bootstrap TabPanel class
18307  * @cfg {Boolean} active panel active
18308  * @cfg {String} html panel content
18309  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18310  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18311  * @cfg {String} href click to link..
18312  * 
18313  * 
18314  * @constructor
18315  * Create a new TabPanel
18316  * @param {Object} config The config object
18317  */
18318
18319 Roo.bootstrap.TabPanel = function(config){
18320     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18321     this.addEvents({
18322         /**
18323              * @event changed
18324              * Fires when the active status changes
18325              * @param {Roo.bootstrap.TabPanel} this
18326              * @param {Boolean} state the new state
18327             
18328          */
18329         'changed': true,
18330         /**
18331              * @event beforedeactivate
18332              * Fires before a tab is de-activated - can be used to do validation on a form.
18333              * @param {Roo.bootstrap.TabPanel} this
18334              * @return {Boolean} false if there is an error
18335             
18336          */
18337         'beforedeactivate': true
18338      });
18339     
18340     this.tabId = this.tabId || Roo.id();
18341   
18342 };
18343
18344 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18345     
18346     active: false,
18347     html: false,
18348     tabId: false,
18349     navId : false,
18350     href : '',
18351     
18352     getAutoCreate : function(){
18353         var cfg = {
18354             tag: 'div',
18355             // item is needed for carousel - not sure if it has any effect otherwise
18356             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18357             html: this.html || ''
18358         };
18359         
18360         if(this.active){
18361             cfg.cls += ' active';
18362         }
18363         
18364         if(this.tabId){
18365             cfg.tabId = this.tabId;
18366         }
18367         
18368         
18369         return cfg;
18370     },
18371     
18372     initEvents:  function()
18373     {
18374         var p = this.parent();
18375         
18376         this.navId = this.navId || p.navId;
18377         
18378         if (typeof(this.navId) != 'undefined') {
18379             // not really needed.. but just in case.. parent should be a NavGroup.
18380             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18381             
18382             tg.register(this);
18383             
18384             var i = tg.tabs.length - 1;
18385             
18386             if(this.active && tg.bullets > 0 && i < tg.bullets){
18387                 tg.setActiveBullet(i);
18388             }
18389         }
18390         
18391         this.el.on('click', this.onClick, this);
18392         
18393         if(Roo.isTouch){
18394             this.el.on("touchstart", this.onTouchStart, this);
18395             this.el.on("touchmove", this.onTouchMove, this);
18396             this.el.on("touchend", this.onTouchEnd, this);
18397         }
18398         
18399     },
18400     
18401     onRender : function(ct, position)
18402     {
18403         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18404     },
18405     
18406     setActive : function(state)
18407     {
18408         Roo.log("panel - set active " + this.tabId + "=" + state);
18409         
18410         this.active = state;
18411         if (!state) {
18412             this.el.removeClass('active');
18413             
18414         } else  if (!this.el.hasClass('active')) {
18415             this.el.addClass('active');
18416         }
18417         
18418         this.fireEvent('changed', this, state);
18419     },
18420     
18421     onClick : function(e)
18422     {
18423         e.preventDefault();
18424         
18425         if(!this.href.length){
18426             return;
18427         }
18428         
18429         window.location.href = this.href;
18430     },
18431     
18432     startX : 0,
18433     startY : 0,
18434     endX : 0,
18435     endY : 0,
18436     swiping : false,
18437     
18438     onTouchStart : function(e)
18439     {
18440         this.swiping = false;
18441         
18442         this.startX = e.browserEvent.touches[0].clientX;
18443         this.startY = e.browserEvent.touches[0].clientY;
18444     },
18445     
18446     onTouchMove : function(e)
18447     {
18448         this.swiping = true;
18449         
18450         this.endX = e.browserEvent.touches[0].clientX;
18451         this.endY = e.browserEvent.touches[0].clientY;
18452     },
18453     
18454     onTouchEnd : function(e)
18455     {
18456         if(!this.swiping){
18457             this.onClick(e);
18458             return;
18459         }
18460         
18461         var tabGroup = this.parent();
18462         
18463         if(this.endX > this.startX){ // swiping right
18464             tabGroup.showPanelPrev();
18465             return;
18466         }
18467         
18468         if(this.startX > this.endX){ // swiping left
18469             tabGroup.showPanelNext();
18470             return;
18471         }
18472     }
18473     
18474     
18475 });
18476  
18477
18478  
18479
18480  /*
18481  * - LGPL
18482  *
18483  * DateField
18484  * 
18485  */
18486
18487 /**
18488  * @class Roo.bootstrap.DateField
18489  * @extends Roo.bootstrap.Input
18490  * Bootstrap DateField class
18491  * @cfg {Number} weekStart default 0
18492  * @cfg {String} viewMode default empty, (months|years)
18493  * @cfg {String} minViewMode default empty, (months|years)
18494  * @cfg {Number} startDate default -Infinity
18495  * @cfg {Number} endDate default Infinity
18496  * @cfg {Boolean} todayHighlight default false
18497  * @cfg {Boolean} todayBtn default false
18498  * @cfg {Boolean} calendarWeeks default false
18499  * @cfg {Object} daysOfWeekDisabled default empty
18500  * @cfg {Boolean} singleMode default false (true | false)
18501  * 
18502  * @cfg {Boolean} keyboardNavigation default true
18503  * @cfg {String} language default en
18504  * 
18505  * @constructor
18506  * Create a new DateField
18507  * @param {Object} config The config object
18508  */
18509
18510 Roo.bootstrap.DateField = function(config){
18511     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18512      this.addEvents({
18513             /**
18514              * @event show
18515              * Fires when this field show.
18516              * @param {Roo.bootstrap.DateField} this
18517              * @param {Mixed} date The date value
18518              */
18519             show : true,
18520             /**
18521              * @event show
18522              * Fires when this field hide.
18523              * @param {Roo.bootstrap.DateField} this
18524              * @param {Mixed} date The date value
18525              */
18526             hide : true,
18527             /**
18528              * @event select
18529              * Fires when select a date.
18530              * @param {Roo.bootstrap.DateField} this
18531              * @param {Mixed} date The date value
18532              */
18533             select : true,
18534             /**
18535              * @event beforeselect
18536              * Fires when before select a date.
18537              * @param {Roo.bootstrap.DateField} this
18538              * @param {Mixed} date The date value
18539              */
18540             beforeselect : true
18541         });
18542 };
18543
18544 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18545     
18546     /**
18547      * @cfg {String} format
18548      * The default date format string which can be overriden for localization support.  The format must be
18549      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18550      */
18551     format : "m/d/y",
18552     /**
18553      * @cfg {String} altFormats
18554      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18555      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18556      */
18557     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18558     
18559     weekStart : 0,
18560     
18561     viewMode : '',
18562     
18563     minViewMode : '',
18564     
18565     todayHighlight : false,
18566     
18567     todayBtn: false,
18568     
18569     language: 'en',
18570     
18571     keyboardNavigation: true,
18572     
18573     calendarWeeks: false,
18574     
18575     startDate: -Infinity,
18576     
18577     endDate: Infinity,
18578     
18579     daysOfWeekDisabled: [],
18580     
18581     _events: [],
18582     
18583     singleMode : false,
18584     
18585     UTCDate: function()
18586     {
18587         return new Date(Date.UTC.apply(Date, arguments));
18588     },
18589     
18590     UTCToday: function()
18591     {
18592         var today = new Date();
18593         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18594     },
18595     
18596     getDate: function() {
18597             var d = this.getUTCDate();
18598             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18599     },
18600     
18601     getUTCDate: function() {
18602             return this.date;
18603     },
18604     
18605     setDate: function(d) {
18606             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18607     },
18608     
18609     setUTCDate: function(d) {
18610             this.date = d;
18611             this.setValue(this.formatDate(this.date));
18612     },
18613         
18614     onRender: function(ct, position)
18615     {
18616         
18617         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18618         
18619         this.language = this.language || 'en';
18620         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18621         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18622         
18623         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18624         this.format = this.format || 'm/d/y';
18625         this.isInline = false;
18626         this.isInput = true;
18627         this.component = this.el.select('.add-on', true).first() || false;
18628         this.component = (this.component && this.component.length === 0) ? false : this.component;
18629         this.hasInput = this.component && this.inputEl().length;
18630         
18631         if (typeof(this.minViewMode === 'string')) {
18632             switch (this.minViewMode) {
18633                 case 'months':
18634                     this.minViewMode = 1;
18635                     break;
18636                 case 'years':
18637                     this.minViewMode = 2;
18638                     break;
18639                 default:
18640                     this.minViewMode = 0;
18641                     break;
18642             }
18643         }
18644         
18645         if (typeof(this.viewMode === 'string')) {
18646             switch (this.viewMode) {
18647                 case 'months':
18648                     this.viewMode = 1;
18649                     break;
18650                 case 'years':
18651                     this.viewMode = 2;
18652                     break;
18653                 default:
18654                     this.viewMode = 0;
18655                     break;
18656             }
18657         }
18658                 
18659         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18660         
18661 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18662         
18663         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18664         
18665         this.picker().on('mousedown', this.onMousedown, this);
18666         this.picker().on('click', this.onClick, this);
18667         
18668         this.picker().addClass('datepicker-dropdown');
18669         
18670         this.startViewMode = this.viewMode;
18671         
18672         if(this.singleMode){
18673             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18674                 v.setVisibilityMode(Roo.Element.DISPLAY);
18675                 v.hide();
18676             });
18677             
18678             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18679                 v.setStyle('width', '189px');
18680             });
18681         }
18682         
18683         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18684             if(!this.calendarWeeks){
18685                 v.remove();
18686                 return;
18687             }
18688             
18689             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18690             v.attr('colspan', function(i, val){
18691                 return parseInt(val) + 1;
18692             });
18693         });
18694                         
18695         
18696         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18697         
18698         this.setStartDate(this.startDate);
18699         this.setEndDate(this.endDate);
18700         
18701         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18702         
18703         this.fillDow();
18704         this.fillMonths();
18705         this.update();
18706         this.showMode();
18707         
18708         if(this.isInline) {
18709             this.showPopup();
18710         }
18711     },
18712     
18713     picker : function()
18714     {
18715         return this.pickerEl;
18716 //        return this.el.select('.datepicker', true).first();
18717     },
18718     
18719     fillDow: function()
18720     {
18721         var dowCnt = this.weekStart;
18722         
18723         var dow = {
18724             tag: 'tr',
18725             cn: [
18726                 
18727             ]
18728         };
18729         
18730         if(this.calendarWeeks){
18731             dow.cn.push({
18732                 tag: 'th',
18733                 cls: 'cw',
18734                 html: '&nbsp;'
18735             })
18736         }
18737         
18738         while (dowCnt < this.weekStart + 7) {
18739             dow.cn.push({
18740                 tag: 'th',
18741                 cls: 'dow',
18742                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18743             });
18744         }
18745         
18746         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18747     },
18748     
18749     fillMonths: function()
18750     {    
18751         var i = 0;
18752         var months = this.picker().select('>.datepicker-months td', true).first();
18753         
18754         months.dom.innerHTML = '';
18755         
18756         while (i < 12) {
18757             var month = {
18758                 tag: 'span',
18759                 cls: 'month',
18760                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18761             };
18762             
18763             months.createChild(month);
18764         }
18765         
18766     },
18767     
18768     update: function()
18769     {
18770         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;
18771         
18772         if (this.date < this.startDate) {
18773             this.viewDate = new Date(this.startDate);
18774         } else if (this.date > this.endDate) {
18775             this.viewDate = new Date(this.endDate);
18776         } else {
18777             this.viewDate = new Date(this.date);
18778         }
18779         
18780         this.fill();
18781     },
18782     
18783     fill: function() 
18784     {
18785         var d = new Date(this.viewDate),
18786                 year = d.getUTCFullYear(),
18787                 month = d.getUTCMonth(),
18788                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18789                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18790                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18791                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18792                 currentDate = this.date && this.date.valueOf(),
18793                 today = this.UTCToday();
18794         
18795         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18796         
18797 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18798         
18799 //        this.picker.select('>tfoot th.today').
18800 //                                              .text(dates[this.language].today)
18801 //                                              .toggle(this.todayBtn !== false);
18802     
18803         this.updateNavArrows();
18804         this.fillMonths();
18805                                                 
18806         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18807         
18808         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18809          
18810         prevMonth.setUTCDate(day);
18811         
18812         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18813         
18814         var nextMonth = new Date(prevMonth);
18815         
18816         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18817         
18818         nextMonth = nextMonth.valueOf();
18819         
18820         var fillMonths = false;
18821         
18822         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18823         
18824         while(prevMonth.valueOf() <= nextMonth) {
18825             var clsName = '';
18826             
18827             if (prevMonth.getUTCDay() === this.weekStart) {
18828                 if(fillMonths){
18829                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18830                 }
18831                     
18832                 fillMonths = {
18833                     tag: 'tr',
18834                     cn: []
18835                 };
18836                 
18837                 if(this.calendarWeeks){
18838                     // ISO 8601: First week contains first thursday.
18839                     // ISO also states week starts on Monday, but we can be more abstract here.
18840                     var
18841                     // Start of current week: based on weekstart/current date
18842                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18843                     // Thursday of this week
18844                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18845                     // First Thursday of year, year from thursday
18846                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18847                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18848                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18849                     
18850                     fillMonths.cn.push({
18851                         tag: 'td',
18852                         cls: 'cw',
18853                         html: calWeek
18854                     });
18855                 }
18856             }
18857             
18858             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18859                 clsName += ' old';
18860             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18861                 clsName += ' new';
18862             }
18863             if (this.todayHighlight &&
18864                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18865                 prevMonth.getUTCMonth() == today.getMonth() &&
18866                 prevMonth.getUTCDate() == today.getDate()) {
18867                 clsName += ' today';
18868             }
18869             
18870             if (currentDate && prevMonth.valueOf() === currentDate) {
18871                 clsName += ' active';
18872             }
18873             
18874             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18875                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18876                     clsName += ' disabled';
18877             }
18878             
18879             fillMonths.cn.push({
18880                 tag: 'td',
18881                 cls: 'day ' + clsName,
18882                 html: prevMonth.getDate()
18883             });
18884             
18885             prevMonth.setDate(prevMonth.getDate()+1);
18886         }
18887           
18888         var currentYear = this.date && this.date.getUTCFullYear();
18889         var currentMonth = this.date && this.date.getUTCMonth();
18890         
18891         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18892         
18893         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18894             v.removeClass('active');
18895             
18896             if(currentYear === year && k === currentMonth){
18897                 v.addClass('active');
18898             }
18899             
18900             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18901                 v.addClass('disabled');
18902             }
18903             
18904         });
18905         
18906         
18907         year = parseInt(year/10, 10) * 10;
18908         
18909         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18910         
18911         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18912         
18913         year -= 1;
18914         for (var i = -1; i < 11; i++) {
18915             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18916                 tag: 'span',
18917                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18918                 html: year
18919             });
18920             
18921             year += 1;
18922         }
18923     },
18924     
18925     showMode: function(dir) 
18926     {
18927         if (dir) {
18928             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18929         }
18930         
18931         Roo.each(this.picker().select('>div',true).elements, function(v){
18932             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18933             v.hide();
18934         });
18935         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18936     },
18937     
18938     place: function()
18939     {
18940         if(this.isInline) {
18941             return;
18942         }
18943         
18944         this.picker().removeClass(['bottom', 'top']);
18945         
18946         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18947             /*
18948              * place to the top of element!
18949              *
18950              */
18951             
18952             this.picker().addClass('top');
18953             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18954             
18955             return;
18956         }
18957         
18958         this.picker().addClass('bottom');
18959         
18960         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18961     },
18962     
18963     parseDate : function(value)
18964     {
18965         if(!value || value instanceof Date){
18966             return value;
18967         }
18968         var v = Date.parseDate(value, this.format);
18969         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18970             v = Date.parseDate(value, 'Y-m-d');
18971         }
18972         if(!v && this.altFormats){
18973             if(!this.altFormatsArray){
18974                 this.altFormatsArray = this.altFormats.split("|");
18975             }
18976             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18977                 v = Date.parseDate(value, this.altFormatsArray[i]);
18978             }
18979         }
18980         return v;
18981     },
18982     
18983     formatDate : function(date, fmt)
18984     {   
18985         return (!date || !(date instanceof Date)) ?
18986         date : date.dateFormat(fmt || this.format);
18987     },
18988     
18989     onFocus : function()
18990     {
18991         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18992         this.showPopup();
18993     },
18994     
18995     onBlur : function()
18996     {
18997         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18998         
18999         var d = this.inputEl().getValue();
19000         
19001         this.setValue(d);
19002                 
19003         this.hidePopup();
19004     },
19005     
19006     showPopup : function()
19007     {
19008         this.picker().show();
19009         this.update();
19010         this.place();
19011         
19012         this.fireEvent('showpopup', this, this.date);
19013     },
19014     
19015     hidePopup : function()
19016     {
19017         if(this.isInline) {
19018             return;
19019         }
19020         this.picker().hide();
19021         this.viewMode = this.startViewMode;
19022         this.showMode();
19023         
19024         this.fireEvent('hidepopup', this, this.date);
19025         
19026     },
19027     
19028     onMousedown: function(e)
19029     {
19030         e.stopPropagation();
19031         e.preventDefault();
19032     },
19033     
19034     keyup: function(e)
19035     {
19036         Roo.bootstrap.DateField.superclass.keyup.call(this);
19037         this.update();
19038     },
19039
19040     setValue: function(v)
19041     {
19042         if(this.fireEvent('beforeselect', this, v) !== false){
19043             var d = new Date(this.parseDate(v) ).clearTime();
19044         
19045             if(isNaN(d.getTime())){
19046                 this.date = this.viewDate = '';
19047                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19048                 return;
19049             }
19050
19051             v = this.formatDate(d);
19052
19053             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19054
19055             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19056
19057             this.update();
19058
19059             this.fireEvent('select', this, this.date);
19060         }
19061     },
19062     
19063     getValue: function()
19064     {
19065         return this.formatDate(this.date);
19066     },
19067     
19068     fireKey: function(e)
19069     {
19070         if (!this.picker().isVisible()){
19071             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19072                 this.showPopup();
19073             }
19074             return;
19075         }
19076         
19077         var dateChanged = false,
19078         dir, day, month,
19079         newDate, newViewDate;
19080         
19081         switch(e.keyCode){
19082             case 27: // escape
19083                 this.hidePopup();
19084                 e.preventDefault();
19085                 break;
19086             case 37: // left
19087             case 39: // right
19088                 if (!this.keyboardNavigation) {
19089                     break;
19090                 }
19091                 dir = e.keyCode == 37 ? -1 : 1;
19092                 
19093                 if (e.ctrlKey){
19094                     newDate = this.moveYear(this.date, dir);
19095                     newViewDate = this.moveYear(this.viewDate, dir);
19096                 } else if (e.shiftKey){
19097                     newDate = this.moveMonth(this.date, dir);
19098                     newViewDate = this.moveMonth(this.viewDate, dir);
19099                 } else {
19100                     newDate = new Date(this.date);
19101                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19102                     newViewDate = new Date(this.viewDate);
19103                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19104                 }
19105                 if (this.dateWithinRange(newDate)){
19106                     this.date = newDate;
19107                     this.viewDate = newViewDate;
19108                     this.setValue(this.formatDate(this.date));
19109 //                    this.update();
19110                     e.preventDefault();
19111                     dateChanged = true;
19112                 }
19113                 break;
19114             case 38: // up
19115             case 40: // down
19116                 if (!this.keyboardNavigation) {
19117                     break;
19118                 }
19119                 dir = e.keyCode == 38 ? -1 : 1;
19120                 if (e.ctrlKey){
19121                     newDate = this.moveYear(this.date, dir);
19122                     newViewDate = this.moveYear(this.viewDate, dir);
19123                 } else if (e.shiftKey){
19124                     newDate = this.moveMonth(this.date, dir);
19125                     newViewDate = this.moveMonth(this.viewDate, dir);
19126                 } else {
19127                     newDate = new Date(this.date);
19128                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19129                     newViewDate = new Date(this.viewDate);
19130                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19131                 }
19132                 if (this.dateWithinRange(newDate)){
19133                     this.date = newDate;
19134                     this.viewDate = newViewDate;
19135                     this.setValue(this.formatDate(this.date));
19136 //                    this.update();
19137                     e.preventDefault();
19138                     dateChanged = true;
19139                 }
19140                 break;
19141             case 13: // enter
19142                 this.setValue(this.formatDate(this.date));
19143                 this.hidePopup();
19144                 e.preventDefault();
19145                 break;
19146             case 9: // tab
19147                 this.setValue(this.formatDate(this.date));
19148                 this.hidePopup();
19149                 break;
19150             case 16: // shift
19151             case 17: // ctrl
19152             case 18: // alt
19153                 break;
19154             default :
19155                 this.hide();
19156                 
19157         }
19158     },
19159     
19160     
19161     onClick: function(e) 
19162     {
19163         e.stopPropagation();
19164         e.preventDefault();
19165         
19166         var target = e.getTarget();
19167         
19168         if(target.nodeName.toLowerCase() === 'i'){
19169             target = Roo.get(target).dom.parentNode;
19170         }
19171         
19172         var nodeName = target.nodeName;
19173         var className = target.className;
19174         var html = target.innerHTML;
19175         //Roo.log(nodeName);
19176         
19177         switch(nodeName.toLowerCase()) {
19178             case 'th':
19179                 switch(className) {
19180                     case 'switch':
19181                         this.showMode(1);
19182                         break;
19183                     case 'prev':
19184                     case 'next':
19185                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19186                         switch(this.viewMode){
19187                                 case 0:
19188                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19189                                         break;
19190                                 case 1:
19191                                 case 2:
19192                                         this.viewDate = this.moveYear(this.viewDate, dir);
19193                                         break;
19194                         }
19195                         this.fill();
19196                         break;
19197                     case 'today':
19198                         var date = new Date();
19199                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19200 //                        this.fill()
19201                         this.setValue(this.formatDate(this.date));
19202                         
19203                         this.hidePopup();
19204                         break;
19205                 }
19206                 break;
19207             case 'span':
19208                 if (className.indexOf('disabled') < 0) {
19209                     this.viewDate.setUTCDate(1);
19210                     if (className.indexOf('month') > -1) {
19211                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19212                     } else {
19213                         var year = parseInt(html, 10) || 0;
19214                         this.viewDate.setUTCFullYear(year);
19215                         
19216                     }
19217                     
19218                     if(this.singleMode){
19219                         this.setValue(this.formatDate(this.viewDate));
19220                         this.hidePopup();
19221                         return;
19222                     }
19223                     
19224                     this.showMode(-1);
19225                     this.fill();
19226                 }
19227                 break;
19228                 
19229             case 'td':
19230                 //Roo.log(className);
19231                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19232                     var day = parseInt(html, 10) || 1;
19233                     var year = this.viewDate.getUTCFullYear(),
19234                         month = this.viewDate.getUTCMonth();
19235
19236                     if (className.indexOf('old') > -1) {
19237                         if(month === 0 ){
19238                             month = 11;
19239                             year -= 1;
19240                         }else{
19241                             month -= 1;
19242                         }
19243                     } else if (className.indexOf('new') > -1) {
19244                         if (month == 11) {
19245                             month = 0;
19246                             year += 1;
19247                         } else {
19248                             month += 1;
19249                         }
19250                     }
19251                     //Roo.log([year,month,day]);
19252                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19253                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19254 //                    this.fill();
19255                     //Roo.log(this.formatDate(this.date));
19256                     this.setValue(this.formatDate(this.date));
19257                     this.hidePopup();
19258                 }
19259                 break;
19260         }
19261     },
19262     
19263     setStartDate: function(startDate)
19264     {
19265         this.startDate = startDate || -Infinity;
19266         if (this.startDate !== -Infinity) {
19267             this.startDate = this.parseDate(this.startDate);
19268         }
19269         this.update();
19270         this.updateNavArrows();
19271     },
19272
19273     setEndDate: function(endDate)
19274     {
19275         this.endDate = endDate || Infinity;
19276         if (this.endDate !== Infinity) {
19277             this.endDate = this.parseDate(this.endDate);
19278         }
19279         this.update();
19280         this.updateNavArrows();
19281     },
19282     
19283     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19284     {
19285         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19286         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19287             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19288         }
19289         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19290             return parseInt(d, 10);
19291         });
19292         this.update();
19293         this.updateNavArrows();
19294     },
19295     
19296     updateNavArrows: function() 
19297     {
19298         if(this.singleMode){
19299             return;
19300         }
19301         
19302         var d = new Date(this.viewDate),
19303         year = d.getUTCFullYear(),
19304         month = d.getUTCMonth();
19305         
19306         Roo.each(this.picker().select('.prev', true).elements, function(v){
19307             v.show();
19308             switch (this.viewMode) {
19309                 case 0:
19310
19311                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19312                         v.hide();
19313                     }
19314                     break;
19315                 case 1:
19316                 case 2:
19317                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19318                         v.hide();
19319                     }
19320                     break;
19321             }
19322         });
19323         
19324         Roo.each(this.picker().select('.next', true).elements, function(v){
19325             v.show();
19326             switch (this.viewMode) {
19327                 case 0:
19328
19329                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19330                         v.hide();
19331                     }
19332                     break;
19333                 case 1:
19334                 case 2:
19335                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19336                         v.hide();
19337                     }
19338                     break;
19339             }
19340         })
19341     },
19342     
19343     moveMonth: function(date, dir)
19344     {
19345         if (!dir) {
19346             return date;
19347         }
19348         var new_date = new Date(date.valueOf()),
19349         day = new_date.getUTCDate(),
19350         month = new_date.getUTCMonth(),
19351         mag = Math.abs(dir),
19352         new_month, test;
19353         dir = dir > 0 ? 1 : -1;
19354         if (mag == 1){
19355             test = dir == -1
19356             // If going back one month, make sure month is not current month
19357             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19358             ? function(){
19359                 return new_date.getUTCMonth() == month;
19360             }
19361             // If going forward one month, make sure month is as expected
19362             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19363             : function(){
19364                 return new_date.getUTCMonth() != new_month;
19365             };
19366             new_month = month + dir;
19367             new_date.setUTCMonth(new_month);
19368             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19369             if (new_month < 0 || new_month > 11) {
19370                 new_month = (new_month + 12) % 12;
19371             }
19372         } else {
19373             // For magnitudes >1, move one month at a time...
19374             for (var i=0; i<mag; i++) {
19375                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19376                 new_date = this.moveMonth(new_date, dir);
19377             }
19378             // ...then reset the day, keeping it in the new month
19379             new_month = new_date.getUTCMonth();
19380             new_date.setUTCDate(day);
19381             test = function(){
19382                 return new_month != new_date.getUTCMonth();
19383             };
19384         }
19385         // Common date-resetting loop -- if date is beyond end of month, make it
19386         // end of month
19387         while (test()){
19388             new_date.setUTCDate(--day);
19389             new_date.setUTCMonth(new_month);
19390         }
19391         return new_date;
19392     },
19393
19394     moveYear: function(date, dir)
19395     {
19396         return this.moveMonth(date, dir*12);
19397     },
19398
19399     dateWithinRange: function(date)
19400     {
19401         return date >= this.startDate && date <= this.endDate;
19402     },
19403
19404     
19405     remove: function() 
19406     {
19407         this.picker().remove();
19408     },
19409     
19410     validateValue : function(value)
19411     {
19412         if(this.getVisibilityEl().hasClass('hidden')){
19413             return true;
19414         }
19415         
19416         if(value.length < 1)  {
19417             if(this.allowBlank){
19418                 return true;
19419             }
19420             return false;
19421         }
19422         
19423         if(value.length < this.minLength){
19424             return false;
19425         }
19426         if(value.length > this.maxLength){
19427             return false;
19428         }
19429         if(this.vtype){
19430             var vt = Roo.form.VTypes;
19431             if(!vt[this.vtype](value, this)){
19432                 return false;
19433             }
19434         }
19435         if(typeof this.validator == "function"){
19436             var msg = this.validator(value);
19437             if(msg !== true){
19438                 return false;
19439             }
19440         }
19441         
19442         if(this.regex && !this.regex.test(value)){
19443             return false;
19444         }
19445         
19446         if(typeof(this.parseDate(value)) == 'undefined'){
19447             return false;
19448         }
19449         
19450         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19451             return false;
19452         }      
19453         
19454         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19455             return false;
19456         } 
19457         
19458         
19459         return true;
19460     },
19461     
19462     reset : function()
19463     {
19464         this.date = this.viewDate = '';
19465         
19466         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19467     }
19468    
19469 });
19470
19471 Roo.apply(Roo.bootstrap.DateField,  {
19472     
19473     head : {
19474         tag: 'thead',
19475         cn: [
19476         {
19477             tag: 'tr',
19478             cn: [
19479             {
19480                 tag: 'th',
19481                 cls: 'prev',
19482                 html: '<i class="fa fa-arrow-left"/>'
19483             },
19484             {
19485                 tag: 'th',
19486                 cls: 'switch',
19487                 colspan: '5'
19488             },
19489             {
19490                 tag: 'th',
19491                 cls: 'next',
19492                 html: '<i class="fa fa-arrow-right"/>'
19493             }
19494
19495             ]
19496         }
19497         ]
19498     },
19499     
19500     content : {
19501         tag: 'tbody',
19502         cn: [
19503         {
19504             tag: 'tr',
19505             cn: [
19506             {
19507                 tag: 'td',
19508                 colspan: '7'
19509             }
19510             ]
19511         }
19512         ]
19513     },
19514     
19515     footer : {
19516         tag: 'tfoot',
19517         cn: [
19518         {
19519             tag: 'tr',
19520             cn: [
19521             {
19522                 tag: 'th',
19523                 colspan: '7',
19524                 cls: 'today'
19525             }
19526                     
19527             ]
19528         }
19529         ]
19530     },
19531     
19532     dates:{
19533         en: {
19534             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19535             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19536             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19537             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19538             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19539             today: "Today"
19540         }
19541     },
19542     
19543     modes: [
19544     {
19545         clsName: 'days',
19546         navFnc: 'Month',
19547         navStep: 1
19548     },
19549     {
19550         clsName: 'months',
19551         navFnc: 'FullYear',
19552         navStep: 1
19553     },
19554     {
19555         clsName: 'years',
19556         navFnc: 'FullYear',
19557         navStep: 10
19558     }]
19559 });
19560
19561 Roo.apply(Roo.bootstrap.DateField,  {
19562   
19563     template : {
19564         tag: 'div',
19565         cls: 'datepicker dropdown-menu roo-dynamic',
19566         cn: [
19567         {
19568             tag: 'div',
19569             cls: 'datepicker-days',
19570             cn: [
19571             {
19572                 tag: 'table',
19573                 cls: 'table-condensed',
19574                 cn:[
19575                 Roo.bootstrap.DateField.head,
19576                 {
19577                     tag: 'tbody'
19578                 },
19579                 Roo.bootstrap.DateField.footer
19580                 ]
19581             }
19582             ]
19583         },
19584         {
19585             tag: 'div',
19586             cls: 'datepicker-months',
19587             cn: [
19588             {
19589                 tag: 'table',
19590                 cls: 'table-condensed',
19591                 cn:[
19592                 Roo.bootstrap.DateField.head,
19593                 Roo.bootstrap.DateField.content,
19594                 Roo.bootstrap.DateField.footer
19595                 ]
19596             }
19597             ]
19598         },
19599         {
19600             tag: 'div',
19601             cls: 'datepicker-years',
19602             cn: [
19603             {
19604                 tag: 'table',
19605                 cls: 'table-condensed',
19606                 cn:[
19607                 Roo.bootstrap.DateField.head,
19608                 Roo.bootstrap.DateField.content,
19609                 Roo.bootstrap.DateField.footer
19610                 ]
19611             }
19612             ]
19613         }
19614         ]
19615     }
19616 });
19617
19618  
19619
19620  /*
19621  * - LGPL
19622  *
19623  * TimeField
19624  * 
19625  */
19626
19627 /**
19628  * @class Roo.bootstrap.TimeField
19629  * @extends Roo.bootstrap.Input
19630  * Bootstrap DateField class
19631  * 
19632  * 
19633  * @constructor
19634  * Create a new TimeField
19635  * @param {Object} config The config object
19636  */
19637
19638 Roo.bootstrap.TimeField = function(config){
19639     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19640     this.addEvents({
19641             /**
19642              * @event show
19643              * Fires when this field show.
19644              * @param {Roo.bootstrap.DateField} thisthis
19645              * @param {Mixed} date The date value
19646              */
19647             show : true,
19648             /**
19649              * @event show
19650              * Fires when this field hide.
19651              * @param {Roo.bootstrap.DateField} this
19652              * @param {Mixed} date The date value
19653              */
19654             hide : true,
19655             /**
19656              * @event select
19657              * Fires when select a date.
19658              * @param {Roo.bootstrap.DateField} this
19659              * @param {Mixed} date The date value
19660              */
19661             select : true
19662         });
19663 };
19664
19665 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19666     
19667     /**
19668      * @cfg {String} format
19669      * The default time format string which can be overriden for localization support.  The format must be
19670      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19671      */
19672     format : "H:i",
19673        
19674     onRender: function(ct, position)
19675     {
19676         
19677         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19678                 
19679         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19680         
19681         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19682         
19683         this.pop = this.picker().select('>.datepicker-time',true).first();
19684         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19685         
19686         this.picker().on('mousedown', this.onMousedown, this);
19687         this.picker().on('click', this.onClick, this);
19688         
19689         this.picker().addClass('datepicker-dropdown');
19690     
19691         this.fillTime();
19692         this.update();
19693             
19694         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19695         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19696         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19697         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19698         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19699         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19700
19701     },
19702     
19703     fireKey: function(e){
19704         if (!this.picker().isVisible()){
19705             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19706                 this.show();
19707             }
19708             return;
19709         }
19710
19711         e.preventDefault();
19712         
19713         switch(e.keyCode){
19714             case 27: // escape
19715                 this.hide();
19716                 break;
19717             case 37: // left
19718             case 39: // right
19719                 this.onTogglePeriod();
19720                 break;
19721             case 38: // up
19722                 this.onIncrementMinutes();
19723                 break;
19724             case 40: // down
19725                 this.onDecrementMinutes();
19726                 break;
19727             case 13: // enter
19728             case 9: // tab
19729                 this.setTime();
19730                 break;
19731         }
19732     },
19733     
19734     onClick: function(e) {
19735         e.stopPropagation();
19736         e.preventDefault();
19737     },
19738     
19739     picker : function()
19740     {
19741         return this.el.select('.datepicker', true).first();
19742     },
19743     
19744     fillTime: function()
19745     {    
19746         var time = this.pop.select('tbody', true).first();
19747         
19748         time.dom.innerHTML = '';
19749         
19750         time.createChild({
19751             tag: 'tr',
19752             cn: [
19753                 {
19754                     tag: 'td',
19755                     cn: [
19756                         {
19757                             tag: 'a',
19758                             href: '#',
19759                             cls: 'btn',
19760                             cn: [
19761                                 {
19762                                     tag: 'span',
19763                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19764                                 }
19765                             ]
19766                         } 
19767                     ]
19768                 },
19769                 {
19770                     tag: 'td',
19771                     cls: 'separator'
19772                 },
19773                 {
19774                     tag: 'td',
19775                     cn: [
19776                         {
19777                             tag: 'a',
19778                             href: '#',
19779                             cls: 'btn',
19780                             cn: [
19781                                 {
19782                                     tag: 'span',
19783                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19784                                 }
19785                             ]
19786                         }
19787                     ]
19788                 },
19789                 {
19790                     tag: 'td',
19791                     cls: 'separator'
19792                 }
19793             ]
19794         });
19795         
19796         time.createChild({
19797             tag: 'tr',
19798             cn: [
19799                 {
19800                     tag: 'td',
19801                     cn: [
19802                         {
19803                             tag: 'span',
19804                             cls: 'timepicker-hour',
19805                             html: '00'
19806                         }  
19807                     ]
19808                 },
19809                 {
19810                     tag: 'td',
19811                     cls: 'separator',
19812                     html: ':'
19813                 },
19814                 {
19815                     tag: 'td',
19816                     cn: [
19817                         {
19818                             tag: 'span',
19819                             cls: 'timepicker-minute',
19820                             html: '00'
19821                         }  
19822                     ]
19823                 },
19824                 {
19825                     tag: 'td',
19826                     cls: 'separator'
19827                 },
19828                 {
19829                     tag: 'td',
19830                     cn: [
19831                         {
19832                             tag: 'button',
19833                             type: 'button',
19834                             cls: 'btn btn-primary period',
19835                             html: 'AM'
19836                             
19837                         }
19838                     ]
19839                 }
19840             ]
19841         });
19842         
19843         time.createChild({
19844             tag: 'tr',
19845             cn: [
19846                 {
19847                     tag: 'td',
19848                     cn: [
19849                         {
19850                             tag: 'a',
19851                             href: '#',
19852                             cls: 'btn',
19853                             cn: [
19854                                 {
19855                                     tag: 'span',
19856                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19857                                 }
19858                             ]
19859                         }
19860                     ]
19861                 },
19862                 {
19863                     tag: 'td',
19864                     cls: 'separator'
19865                 },
19866                 {
19867                     tag: 'td',
19868                     cn: [
19869                         {
19870                             tag: 'a',
19871                             href: '#',
19872                             cls: 'btn',
19873                             cn: [
19874                                 {
19875                                     tag: 'span',
19876                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19877                                 }
19878                             ]
19879                         }
19880                     ]
19881                 },
19882                 {
19883                     tag: 'td',
19884                     cls: 'separator'
19885                 }
19886             ]
19887         });
19888         
19889     },
19890     
19891     update: function()
19892     {
19893         
19894         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19895         
19896         this.fill();
19897     },
19898     
19899     fill: function() 
19900     {
19901         var hours = this.time.getHours();
19902         var minutes = this.time.getMinutes();
19903         var period = 'AM';
19904         
19905         if(hours > 11){
19906             period = 'PM';
19907         }
19908         
19909         if(hours == 0){
19910             hours = 12;
19911         }
19912         
19913         
19914         if(hours > 12){
19915             hours = hours - 12;
19916         }
19917         
19918         if(hours < 10){
19919             hours = '0' + hours;
19920         }
19921         
19922         if(minutes < 10){
19923             minutes = '0' + minutes;
19924         }
19925         
19926         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19927         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19928         this.pop.select('button', true).first().dom.innerHTML = period;
19929         
19930     },
19931     
19932     place: function()
19933     {   
19934         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19935         
19936         var cls = ['bottom'];
19937         
19938         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19939             cls.pop();
19940             cls.push('top');
19941         }
19942         
19943         cls.push('right');
19944         
19945         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19946             cls.pop();
19947             cls.push('left');
19948         }
19949         
19950         this.picker().addClass(cls.join('-'));
19951         
19952         var _this = this;
19953         
19954         Roo.each(cls, function(c){
19955             if(c == 'bottom'){
19956                 _this.picker().setTop(_this.inputEl().getHeight());
19957                 return;
19958             }
19959             if(c == 'top'){
19960                 _this.picker().setTop(0 - _this.picker().getHeight());
19961                 return;
19962             }
19963             
19964             if(c == 'left'){
19965                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19966                 return;
19967             }
19968             if(c == 'right'){
19969                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19970                 return;
19971             }
19972         });
19973         
19974     },
19975   
19976     onFocus : function()
19977     {
19978         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19979         this.show();
19980     },
19981     
19982     onBlur : function()
19983     {
19984         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19985         this.hide();
19986     },
19987     
19988     show : function()
19989     {
19990         this.picker().show();
19991         this.pop.show();
19992         this.update();
19993         this.place();
19994         
19995         this.fireEvent('show', this, this.date);
19996     },
19997     
19998     hide : function()
19999     {
20000         this.picker().hide();
20001         this.pop.hide();
20002         
20003         this.fireEvent('hide', this, this.date);
20004     },
20005     
20006     setTime : function()
20007     {
20008         this.hide();
20009         this.setValue(this.time.format(this.format));
20010         
20011         this.fireEvent('select', this, this.date);
20012         
20013         
20014     },
20015     
20016     onMousedown: function(e){
20017         e.stopPropagation();
20018         e.preventDefault();
20019     },
20020     
20021     onIncrementHours: function()
20022     {
20023         Roo.log('onIncrementHours');
20024         this.time = this.time.add(Date.HOUR, 1);
20025         this.update();
20026         
20027     },
20028     
20029     onDecrementHours: function()
20030     {
20031         Roo.log('onDecrementHours');
20032         this.time = this.time.add(Date.HOUR, -1);
20033         this.update();
20034     },
20035     
20036     onIncrementMinutes: function()
20037     {
20038         Roo.log('onIncrementMinutes');
20039         this.time = this.time.add(Date.MINUTE, 1);
20040         this.update();
20041     },
20042     
20043     onDecrementMinutes: function()
20044     {
20045         Roo.log('onDecrementMinutes');
20046         this.time = this.time.add(Date.MINUTE, -1);
20047         this.update();
20048     },
20049     
20050     onTogglePeriod: function()
20051     {
20052         Roo.log('onTogglePeriod');
20053         this.time = this.time.add(Date.HOUR, 12);
20054         this.update();
20055     }
20056     
20057    
20058 });
20059
20060 Roo.apply(Roo.bootstrap.TimeField,  {
20061     
20062     content : {
20063         tag: 'tbody',
20064         cn: [
20065             {
20066                 tag: 'tr',
20067                 cn: [
20068                 {
20069                     tag: 'td',
20070                     colspan: '7'
20071                 }
20072                 ]
20073             }
20074         ]
20075     },
20076     
20077     footer : {
20078         tag: 'tfoot',
20079         cn: [
20080             {
20081                 tag: 'tr',
20082                 cn: [
20083                 {
20084                     tag: 'th',
20085                     colspan: '7',
20086                     cls: '',
20087                     cn: [
20088                         {
20089                             tag: 'button',
20090                             cls: 'btn btn-info ok',
20091                             html: 'OK'
20092                         }
20093                     ]
20094                 }
20095
20096                 ]
20097             }
20098         ]
20099     }
20100 });
20101
20102 Roo.apply(Roo.bootstrap.TimeField,  {
20103   
20104     template : {
20105         tag: 'div',
20106         cls: 'datepicker dropdown-menu',
20107         cn: [
20108             {
20109                 tag: 'div',
20110                 cls: 'datepicker-time',
20111                 cn: [
20112                 {
20113                     tag: 'table',
20114                     cls: 'table-condensed',
20115                     cn:[
20116                     Roo.bootstrap.TimeField.content,
20117                     Roo.bootstrap.TimeField.footer
20118                     ]
20119                 }
20120                 ]
20121             }
20122         ]
20123     }
20124 });
20125
20126  
20127
20128  /*
20129  * - LGPL
20130  *
20131  * MonthField
20132  * 
20133  */
20134
20135 /**
20136  * @class Roo.bootstrap.MonthField
20137  * @extends Roo.bootstrap.Input
20138  * Bootstrap MonthField class
20139  * 
20140  * @cfg {String} language default en
20141  * 
20142  * @constructor
20143  * Create a new MonthField
20144  * @param {Object} config The config object
20145  */
20146
20147 Roo.bootstrap.MonthField = function(config){
20148     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20149     
20150     this.addEvents({
20151         /**
20152          * @event show
20153          * Fires when this field show.
20154          * @param {Roo.bootstrap.MonthField} this
20155          * @param {Mixed} date The date value
20156          */
20157         show : true,
20158         /**
20159          * @event show
20160          * Fires when this field hide.
20161          * @param {Roo.bootstrap.MonthField} this
20162          * @param {Mixed} date The date value
20163          */
20164         hide : true,
20165         /**
20166          * @event select
20167          * Fires when select a date.
20168          * @param {Roo.bootstrap.MonthField} this
20169          * @param {String} oldvalue The old value
20170          * @param {String} newvalue The new value
20171          */
20172         select : true
20173     });
20174 };
20175
20176 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20177     
20178     onRender: function(ct, position)
20179     {
20180         
20181         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20182         
20183         this.language = this.language || 'en';
20184         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20185         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20186         
20187         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20188         this.isInline = false;
20189         this.isInput = true;
20190         this.component = this.el.select('.add-on', true).first() || false;
20191         this.component = (this.component && this.component.length === 0) ? false : this.component;
20192         this.hasInput = this.component && this.inputEL().length;
20193         
20194         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20195         
20196         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20197         
20198         this.picker().on('mousedown', this.onMousedown, this);
20199         this.picker().on('click', this.onClick, this);
20200         
20201         this.picker().addClass('datepicker-dropdown');
20202         
20203         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20204             v.setStyle('width', '189px');
20205         });
20206         
20207         this.fillMonths();
20208         
20209         this.update();
20210         
20211         if(this.isInline) {
20212             this.show();
20213         }
20214         
20215     },
20216     
20217     setValue: function(v, suppressEvent)
20218     {   
20219         var o = this.getValue();
20220         
20221         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20222         
20223         this.update();
20224
20225         if(suppressEvent !== true){
20226             this.fireEvent('select', this, o, v);
20227         }
20228         
20229     },
20230     
20231     getValue: function()
20232     {
20233         return this.value;
20234     },
20235     
20236     onClick: function(e) 
20237     {
20238         e.stopPropagation();
20239         e.preventDefault();
20240         
20241         var target = e.getTarget();
20242         
20243         if(target.nodeName.toLowerCase() === 'i'){
20244             target = Roo.get(target).dom.parentNode;
20245         }
20246         
20247         var nodeName = target.nodeName;
20248         var className = target.className;
20249         var html = target.innerHTML;
20250         
20251         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20252             return;
20253         }
20254         
20255         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20256         
20257         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20258         
20259         this.hide();
20260                         
20261     },
20262     
20263     picker : function()
20264     {
20265         return this.pickerEl;
20266     },
20267     
20268     fillMonths: function()
20269     {    
20270         var i = 0;
20271         var months = this.picker().select('>.datepicker-months td', true).first();
20272         
20273         months.dom.innerHTML = '';
20274         
20275         while (i < 12) {
20276             var month = {
20277                 tag: 'span',
20278                 cls: 'month',
20279                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20280             };
20281             
20282             months.createChild(month);
20283         }
20284         
20285     },
20286     
20287     update: function()
20288     {
20289         var _this = this;
20290         
20291         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20292             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20293         }
20294         
20295         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20296             e.removeClass('active');
20297             
20298             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20299                 e.addClass('active');
20300             }
20301         })
20302     },
20303     
20304     place: function()
20305     {
20306         if(this.isInline) {
20307             return;
20308         }
20309         
20310         this.picker().removeClass(['bottom', 'top']);
20311         
20312         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20313             /*
20314              * place to the top of element!
20315              *
20316              */
20317             
20318             this.picker().addClass('top');
20319             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20320             
20321             return;
20322         }
20323         
20324         this.picker().addClass('bottom');
20325         
20326         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20327     },
20328     
20329     onFocus : function()
20330     {
20331         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20332         this.show();
20333     },
20334     
20335     onBlur : function()
20336     {
20337         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20338         
20339         var d = this.inputEl().getValue();
20340         
20341         this.setValue(d);
20342                 
20343         this.hide();
20344     },
20345     
20346     show : function()
20347     {
20348         this.picker().show();
20349         this.picker().select('>.datepicker-months', true).first().show();
20350         this.update();
20351         this.place();
20352         
20353         this.fireEvent('show', this, this.date);
20354     },
20355     
20356     hide : function()
20357     {
20358         if(this.isInline) {
20359             return;
20360         }
20361         this.picker().hide();
20362         this.fireEvent('hide', this, this.date);
20363         
20364     },
20365     
20366     onMousedown: function(e)
20367     {
20368         e.stopPropagation();
20369         e.preventDefault();
20370     },
20371     
20372     keyup: function(e)
20373     {
20374         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20375         this.update();
20376     },
20377
20378     fireKey: function(e)
20379     {
20380         if (!this.picker().isVisible()){
20381             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20382                 this.show();
20383             }
20384             return;
20385         }
20386         
20387         var dir;
20388         
20389         switch(e.keyCode){
20390             case 27: // escape
20391                 this.hide();
20392                 e.preventDefault();
20393                 break;
20394             case 37: // left
20395             case 39: // right
20396                 dir = e.keyCode == 37 ? -1 : 1;
20397                 
20398                 this.vIndex = this.vIndex + dir;
20399                 
20400                 if(this.vIndex < 0){
20401                     this.vIndex = 0;
20402                 }
20403                 
20404                 if(this.vIndex > 11){
20405                     this.vIndex = 11;
20406                 }
20407                 
20408                 if(isNaN(this.vIndex)){
20409                     this.vIndex = 0;
20410                 }
20411                 
20412                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20413                 
20414                 break;
20415             case 38: // up
20416             case 40: // down
20417                 
20418                 dir = e.keyCode == 38 ? -1 : 1;
20419                 
20420                 this.vIndex = this.vIndex + dir * 4;
20421                 
20422                 if(this.vIndex < 0){
20423                     this.vIndex = 0;
20424                 }
20425                 
20426                 if(this.vIndex > 11){
20427                     this.vIndex = 11;
20428                 }
20429                 
20430                 if(isNaN(this.vIndex)){
20431                     this.vIndex = 0;
20432                 }
20433                 
20434                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20435                 break;
20436                 
20437             case 13: // enter
20438                 
20439                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20440                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20441                 }
20442                 
20443                 this.hide();
20444                 e.preventDefault();
20445                 break;
20446             case 9: // tab
20447                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20448                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20449                 }
20450                 this.hide();
20451                 break;
20452             case 16: // shift
20453             case 17: // ctrl
20454             case 18: // alt
20455                 break;
20456             default :
20457                 this.hide();
20458                 
20459         }
20460     },
20461     
20462     remove: function() 
20463     {
20464         this.picker().remove();
20465     }
20466    
20467 });
20468
20469 Roo.apply(Roo.bootstrap.MonthField,  {
20470     
20471     content : {
20472         tag: 'tbody',
20473         cn: [
20474         {
20475             tag: 'tr',
20476             cn: [
20477             {
20478                 tag: 'td',
20479                 colspan: '7'
20480             }
20481             ]
20482         }
20483         ]
20484     },
20485     
20486     dates:{
20487         en: {
20488             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20489             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20490         }
20491     }
20492 });
20493
20494 Roo.apply(Roo.bootstrap.MonthField,  {
20495   
20496     template : {
20497         tag: 'div',
20498         cls: 'datepicker dropdown-menu roo-dynamic',
20499         cn: [
20500             {
20501                 tag: 'div',
20502                 cls: 'datepicker-months',
20503                 cn: [
20504                 {
20505                     tag: 'table',
20506                     cls: 'table-condensed',
20507                     cn:[
20508                         Roo.bootstrap.DateField.content
20509                     ]
20510                 }
20511                 ]
20512             }
20513         ]
20514     }
20515 });
20516
20517  
20518
20519  
20520  /*
20521  * - LGPL
20522  *
20523  * CheckBox
20524  * 
20525  */
20526
20527 /**
20528  * @class Roo.bootstrap.CheckBox
20529  * @extends Roo.bootstrap.Input
20530  * Bootstrap CheckBox class
20531  * 
20532  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20533  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20534  * @cfg {String} boxLabel The text that appears beside the checkbox
20535  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20536  * @cfg {Boolean} checked initnal the element
20537  * @cfg {Boolean} inline inline the element (default false)
20538  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20539  * @cfg {String} tooltip label tooltip
20540  * 
20541  * @constructor
20542  * Create a new CheckBox
20543  * @param {Object} config The config object
20544  */
20545
20546 Roo.bootstrap.CheckBox = function(config){
20547     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20548    
20549     this.addEvents({
20550         /**
20551         * @event check
20552         * Fires when the element is checked or unchecked.
20553         * @param {Roo.bootstrap.CheckBox} this This input
20554         * @param {Boolean} checked The new checked value
20555         */
20556        check : true,
20557        /**
20558         * @event click
20559         * Fires when the element is click.
20560         * @param {Roo.bootstrap.CheckBox} this This input
20561         */
20562        click : true
20563     });
20564     
20565 };
20566
20567 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20568   
20569     inputType: 'checkbox',
20570     inputValue: 1,
20571     valueOff: 0,
20572     boxLabel: false,
20573     checked: false,
20574     weight : false,
20575     inline: false,
20576     tooltip : '',
20577     
20578     getAutoCreate : function()
20579     {
20580         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20581         
20582         var id = Roo.id();
20583         
20584         var cfg = {};
20585         
20586         cfg.cls = 'form-group ' + this.inputType; //input-group
20587         
20588         if(this.inline){
20589             cfg.cls += ' ' + this.inputType + '-inline';
20590         }
20591         
20592         var input =  {
20593             tag: 'input',
20594             id : id,
20595             type : this.inputType,
20596             value : this.inputValue,
20597             cls : 'roo-' + this.inputType, //'form-box',
20598             placeholder : this.placeholder || ''
20599             
20600         };
20601         
20602         if(this.inputType != 'radio'){
20603             var hidden =  {
20604                 tag: 'input',
20605                 type : 'hidden',
20606                 cls : 'roo-hidden-value',
20607                 value : this.checked ? this.inputValue : this.valueOff
20608             };
20609         }
20610         
20611             
20612         if (this.weight) { // Validity check?
20613             cfg.cls += " " + this.inputType + "-" + this.weight;
20614         }
20615         
20616         if (this.disabled) {
20617             input.disabled=true;
20618         }
20619         
20620         if(this.checked){
20621             input.checked = this.checked;
20622         }
20623         
20624         if (this.name) {
20625             
20626             input.name = this.name;
20627             
20628             if(this.inputType != 'radio'){
20629                 hidden.name = this.name;
20630                 input.name = '_hidden_' + this.name;
20631             }
20632         }
20633         
20634         if (this.size) {
20635             input.cls += ' input-' + this.size;
20636         }
20637         
20638         var settings=this;
20639         
20640         ['xs','sm','md','lg'].map(function(size){
20641             if (settings[size]) {
20642                 cfg.cls += ' col-' + size + '-' + settings[size];
20643             }
20644         });
20645         
20646         var inputblock = input;
20647          
20648         if (this.before || this.after) {
20649             
20650             inputblock = {
20651                 cls : 'input-group',
20652                 cn :  [] 
20653             };
20654             
20655             if (this.before) {
20656                 inputblock.cn.push({
20657                     tag :'span',
20658                     cls : 'input-group-addon',
20659                     html : this.before
20660                 });
20661             }
20662             
20663             inputblock.cn.push(input);
20664             
20665             if(this.inputType != 'radio'){
20666                 inputblock.cn.push(hidden);
20667             }
20668             
20669             if (this.after) {
20670                 inputblock.cn.push({
20671                     tag :'span',
20672                     cls : 'input-group-addon',
20673                     html : this.after
20674                 });
20675             }
20676             
20677         }
20678         
20679         if (align ==='left' && this.fieldLabel.length) {
20680 //                Roo.log("left and has label");
20681             cfg.cn = [
20682                 {
20683                     tag: 'label',
20684                     'for' :  id,
20685                     cls : 'control-label',
20686                     html : this.fieldLabel
20687                 },
20688                 {
20689                     cls : "", 
20690                     cn: [
20691                         inputblock
20692                     ]
20693                 }
20694             ];
20695             
20696             if(this.labelWidth > 12){
20697                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20698             }
20699             
20700             if(this.labelWidth < 13 && this.labelmd == 0){
20701                 this.labelmd = this.labelWidth;
20702             }
20703             
20704             if(this.labellg > 0){
20705                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20706                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20707             }
20708             
20709             if(this.labelmd > 0){
20710                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20711                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20712             }
20713             
20714             if(this.labelsm > 0){
20715                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20716                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20717             }
20718             
20719             if(this.labelxs > 0){
20720                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20721                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20722             }
20723             
20724         } else if ( this.fieldLabel.length) {
20725 //                Roo.log(" label");
20726                 cfg.cn = [
20727                    
20728                     {
20729                         tag: this.boxLabel ? 'span' : 'label',
20730                         'for': id,
20731                         cls: 'control-label box-input-label',
20732                         //cls : 'input-group-addon',
20733                         html : this.fieldLabel
20734                     },
20735                     
20736                     inputblock
20737                     
20738                 ];
20739
20740         } else {
20741             
20742 //                Roo.log(" no label && no align");
20743                 cfg.cn = [  inputblock ] ;
20744                 
20745                 
20746         }
20747         
20748         if(this.boxLabel){
20749              var boxLabelCfg = {
20750                 tag: 'label',
20751                 //'for': id, // box label is handled by onclick - so no for...
20752                 cls: 'box-label',
20753                 html: this.boxLabel
20754             };
20755             
20756             if(this.tooltip){
20757                 boxLabelCfg.tooltip = this.tooltip;
20758             }
20759              
20760             cfg.cn.push(boxLabelCfg);
20761         }
20762         
20763         if(this.inputType != 'radio'){
20764             cfg.cn.push(hidden);
20765         }
20766         
20767         return cfg;
20768         
20769     },
20770     
20771     /**
20772      * return the real input element.
20773      */
20774     inputEl: function ()
20775     {
20776         return this.el.select('input.roo-' + this.inputType,true).first();
20777     },
20778     hiddenEl: function ()
20779     {
20780         return this.el.select('input.roo-hidden-value',true).first();
20781     },
20782     
20783     labelEl: function()
20784     {
20785         return this.el.select('label.control-label',true).first();
20786     },
20787     /* depricated... */
20788     
20789     label: function()
20790     {
20791         return this.labelEl();
20792     },
20793     
20794     boxLabelEl: function()
20795     {
20796         return this.el.select('label.box-label',true).first();
20797     },
20798     
20799     initEvents : function()
20800     {
20801 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20802         
20803         this.inputEl().on('click', this.onClick,  this);
20804         
20805         if (this.boxLabel) { 
20806             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20807         }
20808         
20809         this.startValue = this.getValue();
20810         
20811         if(this.groupId){
20812             Roo.bootstrap.CheckBox.register(this);
20813         }
20814     },
20815     
20816     onClick : function(e)
20817     {   
20818         if(this.fireEvent('click', this, e) !== false){
20819             this.setChecked(!this.checked);
20820         }
20821         
20822     },
20823     
20824     setChecked : function(state,suppressEvent)
20825     {
20826         this.startValue = this.getValue();
20827
20828         if(this.inputType == 'radio'){
20829             
20830             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20831                 e.dom.checked = false;
20832             });
20833             
20834             this.inputEl().dom.checked = true;
20835             
20836             this.inputEl().dom.value = this.inputValue;
20837             
20838             if(suppressEvent !== true){
20839                 this.fireEvent('check', this, true);
20840             }
20841             
20842             this.validate();
20843             
20844             return;
20845         }
20846         
20847         this.checked = state;
20848         
20849         this.inputEl().dom.checked = state;
20850         
20851         
20852         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20853         
20854         if(suppressEvent !== true){
20855             this.fireEvent('check', this, state);
20856         }
20857         
20858         this.validate();
20859     },
20860     
20861     getValue : function()
20862     {
20863         if(this.inputType == 'radio'){
20864             return this.getGroupValue();
20865         }
20866         
20867         return this.hiddenEl().dom.value;
20868         
20869     },
20870     
20871     getGroupValue : function()
20872     {
20873         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20874             return '';
20875         }
20876         
20877         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20878     },
20879     
20880     setValue : function(v,suppressEvent)
20881     {
20882         if(this.inputType == 'radio'){
20883             this.setGroupValue(v, suppressEvent);
20884             return;
20885         }
20886         
20887         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20888         
20889         this.validate();
20890     },
20891     
20892     setGroupValue : function(v, suppressEvent)
20893     {
20894         this.startValue = this.getValue();
20895         
20896         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20897             e.dom.checked = false;
20898             
20899             if(e.dom.value == v){
20900                 e.dom.checked = true;
20901             }
20902         });
20903         
20904         if(suppressEvent !== true){
20905             this.fireEvent('check', this, true);
20906         }
20907
20908         this.validate();
20909         
20910         return;
20911     },
20912     
20913     validate : function()
20914     {
20915         if(this.getVisibilityEl().hasClass('hidden')){
20916             return true;
20917         }
20918         
20919         if(
20920                 this.disabled || 
20921                 (this.inputType == 'radio' && this.validateRadio()) ||
20922                 (this.inputType == 'checkbox' && this.validateCheckbox())
20923         ){
20924             this.markValid();
20925             return true;
20926         }
20927         
20928         this.markInvalid();
20929         return false;
20930     },
20931     
20932     validateRadio : function()
20933     {
20934         if(this.getVisibilityEl().hasClass('hidden')){
20935             return true;
20936         }
20937         
20938         if(this.allowBlank){
20939             return true;
20940         }
20941         
20942         var valid = false;
20943         
20944         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20945             if(!e.dom.checked){
20946                 return;
20947             }
20948             
20949             valid = true;
20950             
20951             return false;
20952         });
20953         
20954         return valid;
20955     },
20956     
20957     validateCheckbox : function()
20958     {
20959         if(!this.groupId){
20960             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20961             //return (this.getValue() == this.inputValue) ? true : false;
20962         }
20963         
20964         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20965         
20966         if(!group){
20967             return false;
20968         }
20969         
20970         var r = false;
20971         
20972         for(var i in group){
20973             if(group[i].el.isVisible(true)){
20974                 r = false;
20975                 break;
20976             }
20977             
20978             r = true;
20979         }
20980         
20981         for(var i in group){
20982             if(r){
20983                 break;
20984             }
20985             
20986             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20987         }
20988         
20989         return r;
20990     },
20991     
20992     /**
20993      * Mark this field as valid
20994      */
20995     markValid : function()
20996     {
20997         var _this = this;
20998         
20999         this.fireEvent('valid', this);
21000         
21001         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21002         
21003         if(this.groupId){
21004             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21005         }
21006         
21007         if(label){
21008             label.markValid();
21009         }
21010
21011         if(this.inputType == 'radio'){
21012             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21013                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21014                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21015             });
21016             
21017             return;
21018         }
21019
21020         if(!this.groupId){
21021             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21022             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21023             return;
21024         }
21025         
21026         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21027         
21028         if(!group){
21029             return;
21030         }
21031         
21032         for(var i in group){
21033             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21034             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21035         }
21036     },
21037     
21038      /**
21039      * Mark this field as invalid
21040      * @param {String} msg The validation message
21041      */
21042     markInvalid : function(msg)
21043     {
21044         if(this.allowBlank){
21045             return;
21046         }
21047         
21048         var _this = this;
21049         
21050         this.fireEvent('invalid', this, msg);
21051         
21052         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21053         
21054         if(this.groupId){
21055             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21056         }
21057         
21058         if(label){
21059             label.markInvalid();
21060         }
21061             
21062         if(this.inputType == 'radio'){
21063             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21064                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21065                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21066             });
21067             
21068             return;
21069         }
21070         
21071         if(!this.groupId){
21072             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21073             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21074             return;
21075         }
21076         
21077         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21078         
21079         if(!group){
21080             return;
21081         }
21082         
21083         for(var i in group){
21084             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21085             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21086         }
21087         
21088     },
21089     
21090     clearInvalid : function()
21091     {
21092         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21093         
21094         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21095         
21096         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21097         
21098         if (label && label.iconEl) {
21099             label.iconEl.removeClass(label.validClass);
21100             label.iconEl.removeClass(label.invalidClass);
21101         }
21102     },
21103     
21104     disable : function()
21105     {
21106         if(this.inputType != 'radio'){
21107             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21108             return;
21109         }
21110         
21111         var _this = this;
21112         
21113         if(this.rendered){
21114             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21115                 _this.getActionEl().addClass(this.disabledClass);
21116                 e.dom.disabled = true;
21117             });
21118         }
21119         
21120         this.disabled = true;
21121         this.fireEvent("disable", this);
21122         return this;
21123     },
21124
21125     enable : function()
21126     {
21127         if(this.inputType != 'radio'){
21128             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21129             return;
21130         }
21131         
21132         var _this = this;
21133         
21134         if(this.rendered){
21135             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21136                 _this.getActionEl().removeClass(this.disabledClass);
21137                 e.dom.disabled = false;
21138             });
21139         }
21140         
21141         this.disabled = false;
21142         this.fireEvent("enable", this);
21143         return this;
21144     },
21145     
21146     setBoxLabel : function(v)
21147     {
21148         this.boxLabel = v;
21149         
21150         if(this.rendered){
21151             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21152         }
21153     }
21154
21155 });
21156
21157 Roo.apply(Roo.bootstrap.CheckBox, {
21158     
21159     groups: {},
21160     
21161      /**
21162     * register a CheckBox Group
21163     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21164     */
21165     register : function(checkbox)
21166     {
21167         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21168             this.groups[checkbox.groupId] = {};
21169         }
21170         
21171         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21172             return;
21173         }
21174         
21175         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21176         
21177     },
21178     /**
21179     * fetch a CheckBox Group based on the group ID
21180     * @param {string} the group ID
21181     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21182     */
21183     get: function(groupId) {
21184         if (typeof(this.groups[groupId]) == 'undefined') {
21185             return false;
21186         }
21187         
21188         return this.groups[groupId] ;
21189     }
21190     
21191     
21192 });
21193 /*
21194  * - LGPL
21195  *
21196  * RadioItem
21197  * 
21198  */
21199
21200 /**
21201  * @class Roo.bootstrap.Radio
21202  * @extends Roo.bootstrap.Component
21203  * Bootstrap Radio class
21204  * @cfg {String} boxLabel - the label associated
21205  * @cfg {String} value - the value of radio
21206  * 
21207  * @constructor
21208  * Create a new Radio
21209  * @param {Object} config The config object
21210  */
21211 Roo.bootstrap.Radio = function(config){
21212     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21213     
21214 };
21215
21216 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21217     
21218     boxLabel : '',
21219     
21220     value : '',
21221     
21222     getAutoCreate : function()
21223     {
21224         var cfg = {
21225             tag : 'div',
21226             cls : 'form-group radio',
21227             cn : [
21228                 {
21229                     tag : 'label',
21230                     cls : 'box-label',
21231                     html : this.boxLabel
21232                 }
21233             ]
21234         };
21235         
21236         return cfg;
21237     },
21238     
21239     initEvents : function() 
21240     {
21241         this.parent().register(this);
21242         
21243         this.el.on('click', this.onClick, this);
21244         
21245     },
21246     
21247     onClick : function(e)
21248     {
21249         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21250             this.setChecked(true);
21251         }
21252     },
21253     
21254     setChecked : function(state, suppressEvent)
21255     {
21256         this.parent().setValue(this.value, suppressEvent);
21257         
21258     },
21259     
21260     setBoxLabel : function(v)
21261     {
21262         this.boxLabel = v;
21263         
21264         if(this.rendered){
21265             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21266         }
21267     }
21268     
21269 });
21270  
21271
21272  /*
21273  * - LGPL
21274  *
21275  * Input
21276  * 
21277  */
21278
21279 /**
21280  * @class Roo.bootstrap.SecurePass
21281  * @extends Roo.bootstrap.Input
21282  * Bootstrap SecurePass class
21283  *
21284  * 
21285  * @constructor
21286  * Create a new SecurePass
21287  * @param {Object} config The config object
21288  */
21289  
21290 Roo.bootstrap.SecurePass = function (config) {
21291     // these go here, so the translation tool can replace them..
21292     this.errors = {
21293         PwdEmpty: "Please type a password, and then retype it to confirm.",
21294         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21295         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21296         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21297         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21298         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21299         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21300         TooWeak: "Your password is Too Weak."
21301     },
21302     this.meterLabel = "Password strength:";
21303     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21304     this.meterClass = [
21305         "roo-password-meter-tooweak", 
21306         "roo-password-meter-weak", 
21307         "roo-password-meter-medium", 
21308         "roo-password-meter-strong", 
21309         "roo-password-meter-grey"
21310     ];
21311     
21312     this.errors = {};
21313     
21314     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21315 }
21316
21317 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21318     /**
21319      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21320      * {
21321      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21322      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21323      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21324      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21325      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21326      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21327      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21328      * })
21329      */
21330     // private
21331     
21332     meterWidth: 300,
21333     errorMsg :'',    
21334     errors: false,
21335     imageRoot: '/',
21336     /**
21337      * @cfg {String/Object} Label for the strength meter (defaults to
21338      * 'Password strength:')
21339      */
21340     // private
21341     meterLabel: '',
21342     /**
21343      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21344      * ['Weak', 'Medium', 'Strong'])
21345      */
21346     // private    
21347     pwdStrengths: false,    
21348     // private
21349     strength: 0,
21350     // private
21351     _lastPwd: null,
21352     // private
21353     kCapitalLetter: 0,
21354     kSmallLetter: 1,
21355     kDigit: 2,
21356     kPunctuation: 3,
21357     
21358     insecure: false,
21359     // private
21360     initEvents: function ()
21361     {
21362         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21363
21364         if (this.el.is('input[type=password]') && Roo.isSafari) {
21365             this.el.on('keydown', this.SafariOnKeyDown, this);
21366         }
21367
21368         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21369     },
21370     // private
21371     onRender: function (ct, position)
21372     {
21373         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21374         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21375         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21376
21377         this.trigger.createChild({
21378                    cn: [
21379                     {
21380                     //id: 'PwdMeter',
21381                     tag: 'div',
21382                     cls: 'roo-password-meter-grey col-xs-12',
21383                     style: {
21384                         //width: 0,
21385                         //width: this.meterWidth + 'px'                                                
21386                         }
21387                     },
21388                     {                            
21389                          cls: 'roo-password-meter-text'                          
21390                     }
21391                 ]            
21392         });
21393
21394          
21395         if (this.hideTrigger) {
21396             this.trigger.setDisplayed(false);
21397         }
21398         this.setSize(this.width || '', this.height || '');
21399     },
21400     // private
21401     onDestroy: function ()
21402     {
21403         if (this.trigger) {
21404             this.trigger.removeAllListeners();
21405             this.trigger.remove();
21406         }
21407         if (this.wrap) {
21408             this.wrap.remove();
21409         }
21410         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21411     },
21412     // private
21413     checkStrength: function ()
21414     {
21415         var pwd = this.inputEl().getValue();
21416         if (pwd == this._lastPwd) {
21417             return;
21418         }
21419
21420         var strength;
21421         if (this.ClientSideStrongPassword(pwd)) {
21422             strength = 3;
21423         } else if (this.ClientSideMediumPassword(pwd)) {
21424             strength = 2;
21425         } else if (this.ClientSideWeakPassword(pwd)) {
21426             strength = 1;
21427         } else {
21428             strength = 0;
21429         }
21430         
21431         Roo.log('strength1: ' + strength);
21432         
21433         //var pm = this.trigger.child('div/div/div').dom;
21434         var pm = this.trigger.child('div/div');
21435         pm.removeClass(this.meterClass);
21436         pm.addClass(this.meterClass[strength]);
21437                 
21438         
21439         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21440                 
21441         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21442         
21443         this._lastPwd = pwd;
21444     },
21445     reset: function ()
21446     {
21447         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21448         
21449         this._lastPwd = '';
21450         
21451         var pm = this.trigger.child('div/div');
21452         pm.removeClass(this.meterClass);
21453         pm.addClass('roo-password-meter-grey');        
21454         
21455         
21456         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21457         
21458         pt.innerHTML = '';
21459         this.inputEl().dom.type='password';
21460     },
21461     // private
21462     validateValue: function (value)
21463     {
21464         
21465         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21466             return false;
21467         }
21468         if (value.length == 0) {
21469             if (this.allowBlank) {
21470                 this.clearInvalid();
21471                 return true;
21472             }
21473
21474             this.markInvalid(this.errors.PwdEmpty);
21475             this.errorMsg = this.errors.PwdEmpty;
21476             return false;
21477         }
21478         
21479         if(this.insecure){
21480             return true;
21481         }
21482         
21483         if ('[\x21-\x7e]*'.match(value)) {
21484             this.markInvalid(this.errors.PwdBadChar);
21485             this.errorMsg = this.errors.PwdBadChar;
21486             return false;
21487         }
21488         if (value.length < 6) {
21489             this.markInvalid(this.errors.PwdShort);
21490             this.errorMsg = this.errors.PwdShort;
21491             return false;
21492         }
21493         if (value.length > 16) {
21494             this.markInvalid(this.errors.PwdLong);
21495             this.errorMsg = this.errors.PwdLong;
21496             return false;
21497         }
21498         var strength;
21499         if (this.ClientSideStrongPassword(value)) {
21500             strength = 3;
21501         } else if (this.ClientSideMediumPassword(value)) {
21502             strength = 2;
21503         } else if (this.ClientSideWeakPassword(value)) {
21504             strength = 1;
21505         } else {
21506             strength = 0;
21507         }
21508
21509         
21510         if (strength < 2) {
21511             //this.markInvalid(this.errors.TooWeak);
21512             this.errorMsg = this.errors.TooWeak;
21513             //return false;
21514         }
21515         
21516         
21517         console.log('strength2: ' + strength);
21518         
21519         //var pm = this.trigger.child('div/div/div').dom;
21520         
21521         var pm = this.trigger.child('div/div');
21522         pm.removeClass(this.meterClass);
21523         pm.addClass(this.meterClass[strength]);
21524                 
21525         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21526                 
21527         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21528         
21529         this.errorMsg = ''; 
21530         return true;
21531     },
21532     // private
21533     CharacterSetChecks: function (type)
21534     {
21535         this.type = type;
21536         this.fResult = false;
21537     },
21538     // private
21539     isctype: function (character, type)
21540     {
21541         switch (type) {  
21542             case this.kCapitalLetter:
21543                 if (character >= 'A' && character <= 'Z') {
21544                     return true;
21545                 }
21546                 break;
21547             
21548             case this.kSmallLetter:
21549                 if (character >= 'a' && character <= 'z') {
21550                     return true;
21551                 }
21552                 break;
21553             
21554             case this.kDigit:
21555                 if (character >= '0' && character <= '9') {
21556                     return true;
21557                 }
21558                 break;
21559             
21560             case this.kPunctuation:
21561                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21562                     return true;
21563                 }
21564                 break;
21565             
21566             default:
21567                 return false;
21568         }
21569
21570     },
21571     // private
21572     IsLongEnough: function (pwd, size)
21573     {
21574         return !(pwd == null || isNaN(size) || pwd.length < size);
21575     },
21576     // private
21577     SpansEnoughCharacterSets: function (word, nb)
21578     {
21579         if (!this.IsLongEnough(word, nb))
21580         {
21581             return false;
21582         }
21583
21584         var characterSetChecks = new Array(
21585             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21586             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21587         );
21588         
21589         for (var index = 0; index < word.length; ++index) {
21590             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21591                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21592                     characterSetChecks[nCharSet].fResult = true;
21593                     break;
21594                 }
21595             }
21596         }
21597
21598         var nCharSets = 0;
21599         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21600             if (characterSetChecks[nCharSet].fResult) {
21601                 ++nCharSets;
21602             }
21603         }
21604
21605         if (nCharSets < nb) {
21606             return false;
21607         }
21608         return true;
21609     },
21610     // private
21611     ClientSideStrongPassword: function (pwd)
21612     {
21613         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21614     },
21615     // private
21616     ClientSideMediumPassword: function (pwd)
21617     {
21618         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21619     },
21620     // private
21621     ClientSideWeakPassword: function (pwd)
21622     {
21623         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21624     }
21625           
21626 })//<script type="text/javascript">
21627
21628 /*
21629  * Based  Ext JS Library 1.1.1
21630  * Copyright(c) 2006-2007, Ext JS, LLC.
21631  * LGPL
21632  *
21633  */
21634  
21635 /**
21636  * @class Roo.HtmlEditorCore
21637  * @extends Roo.Component
21638  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21639  *
21640  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21641  */
21642
21643 Roo.HtmlEditorCore = function(config){
21644     
21645     
21646     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21647     
21648     
21649     this.addEvents({
21650         /**
21651          * @event initialize
21652          * Fires when the editor is fully initialized (including the iframe)
21653          * @param {Roo.HtmlEditorCore} this
21654          */
21655         initialize: true,
21656         /**
21657          * @event activate
21658          * Fires when the editor is first receives the focus. Any insertion must wait
21659          * until after this event.
21660          * @param {Roo.HtmlEditorCore} this
21661          */
21662         activate: true,
21663          /**
21664          * @event beforesync
21665          * Fires before the textarea is updated with content from the editor iframe. Return false
21666          * to cancel the sync.
21667          * @param {Roo.HtmlEditorCore} this
21668          * @param {String} html
21669          */
21670         beforesync: true,
21671          /**
21672          * @event beforepush
21673          * Fires before the iframe editor is updated with content from the textarea. Return false
21674          * to cancel the push.
21675          * @param {Roo.HtmlEditorCore} this
21676          * @param {String} html
21677          */
21678         beforepush: true,
21679          /**
21680          * @event sync
21681          * Fires when the textarea is updated with content from the editor iframe.
21682          * @param {Roo.HtmlEditorCore} this
21683          * @param {String} html
21684          */
21685         sync: true,
21686          /**
21687          * @event push
21688          * Fires when the iframe editor is updated with content from the textarea.
21689          * @param {Roo.HtmlEditorCore} this
21690          * @param {String} html
21691          */
21692         push: true,
21693         
21694         /**
21695          * @event editorevent
21696          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21697          * @param {Roo.HtmlEditorCore} this
21698          */
21699         editorevent: true
21700         
21701     });
21702     
21703     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21704     
21705     // defaults : white / black...
21706     this.applyBlacklists();
21707     
21708     
21709     
21710 };
21711
21712
21713 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21714
21715
21716      /**
21717      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21718      */
21719     
21720     owner : false,
21721     
21722      /**
21723      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21724      *                        Roo.resizable.
21725      */
21726     resizable : false,
21727      /**
21728      * @cfg {Number} height (in pixels)
21729      */   
21730     height: 300,
21731    /**
21732      * @cfg {Number} width (in pixels)
21733      */   
21734     width: 500,
21735     
21736     /**
21737      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21738      * 
21739      */
21740     stylesheets: false,
21741     
21742     // id of frame..
21743     frameId: false,
21744     
21745     // private properties
21746     validationEvent : false,
21747     deferHeight: true,
21748     initialized : false,
21749     activated : false,
21750     sourceEditMode : false,
21751     onFocus : Roo.emptyFn,
21752     iframePad:3,
21753     hideMode:'offsets',
21754     
21755     clearUp: true,
21756     
21757     // blacklist + whitelisted elements..
21758     black: false,
21759     white: false,
21760      
21761     bodyCls : '',
21762
21763     /**
21764      * Protected method that will not generally be called directly. It
21765      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21766      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21767      */
21768     getDocMarkup : function(){
21769         // body styles..
21770         var st = '';
21771         
21772         // inherit styels from page...?? 
21773         if (this.stylesheets === false) {
21774             
21775             Roo.get(document.head).select('style').each(function(node) {
21776                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21777             });
21778             
21779             Roo.get(document.head).select('link').each(function(node) { 
21780                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21781             });
21782             
21783         } else if (!this.stylesheets.length) {
21784                 // simple..
21785                 st = '<style type="text/css">' +
21786                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21787                    '</style>';
21788         } else { 
21789             st = '<style type="text/css">' +
21790                     this.stylesheets +
21791                 '</style>';
21792         }
21793         
21794         st +=  '<style type="text/css">' +
21795             'IMG { cursor: pointer } ' +
21796         '</style>';
21797
21798         var cls = 'roo-htmleditor-body';
21799         
21800         if(this.bodyCls.length){
21801             cls += ' ' + this.bodyCls;
21802         }
21803         
21804         return '<html><head>' + st  +
21805             //<style type="text/css">' +
21806             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21807             //'</style>' +
21808             ' </head><body class="' +  cls + '"></body></html>';
21809     },
21810
21811     // private
21812     onRender : function(ct, position)
21813     {
21814         var _t = this;
21815         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21816         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21817         
21818         
21819         this.el.dom.style.border = '0 none';
21820         this.el.dom.setAttribute('tabIndex', -1);
21821         this.el.addClass('x-hidden hide');
21822         
21823         
21824         
21825         if(Roo.isIE){ // fix IE 1px bogus margin
21826             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21827         }
21828        
21829         
21830         this.frameId = Roo.id();
21831         
21832          
21833         
21834         var iframe = this.owner.wrap.createChild({
21835             tag: 'iframe',
21836             cls: 'form-control', // bootstrap..
21837             id: this.frameId,
21838             name: this.frameId,
21839             frameBorder : 'no',
21840             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21841         }, this.el
21842         );
21843         
21844         
21845         this.iframe = iframe.dom;
21846
21847          this.assignDocWin();
21848         
21849         this.doc.designMode = 'on';
21850        
21851         this.doc.open();
21852         this.doc.write(this.getDocMarkup());
21853         this.doc.close();
21854
21855         
21856         var task = { // must defer to wait for browser to be ready
21857             run : function(){
21858                 //console.log("run task?" + this.doc.readyState);
21859                 this.assignDocWin();
21860                 if(this.doc.body || this.doc.readyState == 'complete'){
21861                     try {
21862                         this.doc.designMode="on";
21863                     } catch (e) {
21864                         return;
21865                     }
21866                     Roo.TaskMgr.stop(task);
21867                     this.initEditor.defer(10, this);
21868                 }
21869             },
21870             interval : 10,
21871             duration: 10000,
21872             scope: this
21873         };
21874         Roo.TaskMgr.start(task);
21875
21876     },
21877
21878     // private
21879     onResize : function(w, h)
21880     {
21881          Roo.log('resize: ' +w + ',' + h );
21882         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21883         if(!this.iframe){
21884             return;
21885         }
21886         if(typeof w == 'number'){
21887             
21888             this.iframe.style.width = w + 'px';
21889         }
21890         if(typeof h == 'number'){
21891             
21892             this.iframe.style.height = h + 'px';
21893             if(this.doc){
21894                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21895             }
21896         }
21897         
21898     },
21899
21900     /**
21901      * Toggles the editor between standard and source edit mode.
21902      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21903      */
21904     toggleSourceEdit : function(sourceEditMode){
21905         
21906         this.sourceEditMode = sourceEditMode === true;
21907         
21908         if(this.sourceEditMode){
21909  
21910             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21911             
21912         }else{
21913             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21914             //this.iframe.className = '';
21915             this.deferFocus();
21916         }
21917         //this.setSize(this.owner.wrap.getSize());
21918         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21919     },
21920
21921     
21922   
21923
21924     /**
21925      * Protected method that will not generally be called directly. If you need/want
21926      * custom HTML cleanup, this is the method you should override.
21927      * @param {String} html The HTML to be cleaned
21928      * return {String} The cleaned HTML
21929      */
21930     cleanHtml : function(html){
21931         html = String(html);
21932         if(html.length > 5){
21933             if(Roo.isSafari){ // strip safari nonsense
21934                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21935             }
21936         }
21937         if(html == '&nbsp;'){
21938             html = '';
21939         }
21940         return html;
21941     },
21942
21943     /**
21944      * HTML Editor -> Textarea
21945      * Protected method that will not generally be called directly. Syncs the contents
21946      * of the editor iframe with the textarea.
21947      */
21948     syncValue : function(){
21949         if(this.initialized){
21950             var bd = (this.doc.body || this.doc.documentElement);
21951             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21952             var html = bd.innerHTML;
21953             if(Roo.isSafari){
21954                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21955                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21956                 if(m && m[1]){
21957                     html = '<div style="'+m[0]+'">' + html + '</div>';
21958                 }
21959             }
21960             html = this.cleanHtml(html);
21961             // fix up the special chars.. normaly like back quotes in word...
21962             // however we do not want to do this with chinese..
21963             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21964                 var cc = b.charCodeAt();
21965                 if (
21966                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21967                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21968                     (cc >= 0xf900 && cc < 0xfb00 )
21969                 ) {
21970                         return b;
21971                 }
21972                 return "&#"+cc+";" 
21973             });
21974             if(this.owner.fireEvent('beforesync', this, html) !== false){
21975                 this.el.dom.value = html;
21976                 this.owner.fireEvent('sync', this, html);
21977             }
21978         }
21979     },
21980
21981     /**
21982      * Protected method that will not generally be called directly. Pushes the value of the textarea
21983      * into the iframe editor.
21984      */
21985     pushValue : function(){
21986         if(this.initialized){
21987             var v = this.el.dom.value.trim();
21988             
21989 //            if(v.length < 1){
21990 //                v = '&#160;';
21991 //            }
21992             
21993             if(this.owner.fireEvent('beforepush', this, v) !== false){
21994                 var d = (this.doc.body || this.doc.documentElement);
21995                 d.innerHTML = v;
21996                 this.cleanUpPaste();
21997                 this.el.dom.value = d.innerHTML;
21998                 this.owner.fireEvent('push', this, v);
21999             }
22000         }
22001     },
22002
22003     // private
22004     deferFocus : function(){
22005         this.focus.defer(10, this);
22006     },
22007
22008     // doc'ed in Field
22009     focus : function(){
22010         if(this.win && !this.sourceEditMode){
22011             this.win.focus();
22012         }else{
22013             this.el.focus();
22014         }
22015     },
22016     
22017     assignDocWin: function()
22018     {
22019         var iframe = this.iframe;
22020         
22021          if(Roo.isIE){
22022             this.doc = iframe.contentWindow.document;
22023             this.win = iframe.contentWindow;
22024         } else {
22025 //            if (!Roo.get(this.frameId)) {
22026 //                return;
22027 //            }
22028 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22029 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22030             
22031             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22032                 return;
22033             }
22034             
22035             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22036             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22037         }
22038     },
22039     
22040     // private
22041     initEditor : function(){
22042         //console.log("INIT EDITOR");
22043         this.assignDocWin();
22044         
22045         
22046         
22047         this.doc.designMode="on";
22048         this.doc.open();
22049         this.doc.write(this.getDocMarkup());
22050         this.doc.close();
22051         
22052         var dbody = (this.doc.body || this.doc.documentElement);
22053         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22054         // this copies styles from the containing element into thsi one..
22055         // not sure why we need all of this..
22056         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22057         
22058         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22059         //ss['background-attachment'] = 'fixed'; // w3c
22060         dbody.bgProperties = 'fixed'; // ie
22061         //Roo.DomHelper.applyStyles(dbody, ss);
22062         Roo.EventManager.on(this.doc, {
22063             //'mousedown': this.onEditorEvent,
22064             'mouseup': this.onEditorEvent,
22065             'dblclick': this.onEditorEvent,
22066             'click': this.onEditorEvent,
22067             'keyup': this.onEditorEvent,
22068             buffer:100,
22069             scope: this
22070         });
22071         if(Roo.isGecko){
22072             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22073         }
22074         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22075             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22076         }
22077         this.initialized = true;
22078
22079         this.owner.fireEvent('initialize', this);
22080         this.pushValue();
22081     },
22082
22083     // private
22084     onDestroy : function(){
22085         
22086         
22087         
22088         if(this.rendered){
22089             
22090             //for (var i =0; i < this.toolbars.length;i++) {
22091             //    // fixme - ask toolbars for heights?
22092             //    this.toolbars[i].onDestroy();
22093            // }
22094             
22095             //this.wrap.dom.innerHTML = '';
22096             //this.wrap.remove();
22097         }
22098     },
22099
22100     // private
22101     onFirstFocus : function(){
22102         
22103         this.assignDocWin();
22104         
22105         
22106         this.activated = true;
22107          
22108     
22109         if(Roo.isGecko){ // prevent silly gecko errors
22110             this.win.focus();
22111             var s = this.win.getSelection();
22112             if(!s.focusNode || s.focusNode.nodeType != 3){
22113                 var r = s.getRangeAt(0);
22114                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22115                 r.collapse(true);
22116                 this.deferFocus();
22117             }
22118             try{
22119                 this.execCmd('useCSS', true);
22120                 this.execCmd('styleWithCSS', false);
22121             }catch(e){}
22122         }
22123         this.owner.fireEvent('activate', this);
22124     },
22125
22126     // private
22127     adjustFont: function(btn){
22128         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22129         //if(Roo.isSafari){ // safari
22130         //    adjust *= 2;
22131        // }
22132         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22133         if(Roo.isSafari){ // safari
22134             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22135             v =  (v < 10) ? 10 : v;
22136             v =  (v > 48) ? 48 : v;
22137             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22138             
22139         }
22140         
22141         
22142         v = Math.max(1, v+adjust);
22143         
22144         this.execCmd('FontSize', v  );
22145     },
22146
22147     onEditorEvent : function(e)
22148     {
22149         this.owner.fireEvent('editorevent', this, e);
22150       //  this.updateToolbar();
22151         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22152     },
22153
22154     insertTag : function(tg)
22155     {
22156         // could be a bit smarter... -> wrap the current selected tRoo..
22157         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22158             
22159             range = this.createRange(this.getSelection());
22160             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22161             wrappingNode.appendChild(range.extractContents());
22162             range.insertNode(wrappingNode);
22163
22164             return;
22165             
22166             
22167             
22168         }
22169         this.execCmd("formatblock",   tg);
22170         
22171     },
22172     
22173     insertText : function(txt)
22174     {
22175         
22176         
22177         var range = this.createRange();
22178         range.deleteContents();
22179                //alert(Sender.getAttribute('label'));
22180                
22181         range.insertNode(this.doc.createTextNode(txt));
22182     } ,
22183     
22184      
22185
22186     /**
22187      * Executes a Midas editor command on the editor document and performs necessary focus and
22188      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22189      * @param {String} cmd The Midas command
22190      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22191      */
22192     relayCmd : function(cmd, value){
22193         this.win.focus();
22194         this.execCmd(cmd, value);
22195         this.owner.fireEvent('editorevent', this);
22196         //this.updateToolbar();
22197         this.owner.deferFocus();
22198     },
22199
22200     /**
22201      * Executes a Midas editor command directly on the editor document.
22202      * For visual commands, you should use {@link #relayCmd} instead.
22203      * <b>This should only be called after the editor is initialized.</b>
22204      * @param {String} cmd The Midas command
22205      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22206      */
22207     execCmd : function(cmd, value){
22208         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22209         this.syncValue();
22210     },
22211  
22212  
22213    
22214     /**
22215      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22216      * to insert tRoo.
22217      * @param {String} text | dom node.. 
22218      */
22219     insertAtCursor : function(text)
22220     {
22221         
22222         if(!this.activated){
22223             return;
22224         }
22225         /*
22226         if(Roo.isIE){
22227             this.win.focus();
22228             var r = this.doc.selection.createRange();
22229             if(r){
22230                 r.collapse(true);
22231                 r.pasteHTML(text);
22232                 this.syncValue();
22233                 this.deferFocus();
22234             
22235             }
22236             return;
22237         }
22238         */
22239         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22240             this.win.focus();
22241             
22242             
22243             // from jquery ui (MIT licenced)
22244             var range, node;
22245             var win = this.win;
22246             
22247             if (win.getSelection && win.getSelection().getRangeAt) {
22248                 range = win.getSelection().getRangeAt(0);
22249                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22250                 range.insertNode(node);
22251             } else if (win.document.selection && win.document.selection.createRange) {
22252                 // no firefox support
22253                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22254                 win.document.selection.createRange().pasteHTML(txt);
22255             } else {
22256                 // no firefox support
22257                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22258                 this.execCmd('InsertHTML', txt);
22259             } 
22260             
22261             this.syncValue();
22262             
22263             this.deferFocus();
22264         }
22265     },
22266  // private
22267     mozKeyPress : function(e){
22268         if(e.ctrlKey){
22269             var c = e.getCharCode(), cmd;
22270           
22271             if(c > 0){
22272                 c = String.fromCharCode(c).toLowerCase();
22273                 switch(c){
22274                     case 'b':
22275                         cmd = 'bold';
22276                         break;
22277                     case 'i':
22278                         cmd = 'italic';
22279                         break;
22280                     
22281                     case 'u':
22282                         cmd = 'underline';
22283                         break;
22284                     
22285                     case 'v':
22286                         this.cleanUpPaste.defer(100, this);
22287                         return;
22288                         
22289                 }
22290                 if(cmd){
22291                     this.win.focus();
22292                     this.execCmd(cmd);
22293                     this.deferFocus();
22294                     e.preventDefault();
22295                 }
22296                 
22297             }
22298         }
22299     },
22300
22301     // private
22302     fixKeys : function(){ // load time branching for fastest keydown performance
22303         if(Roo.isIE){
22304             return function(e){
22305                 var k = e.getKey(), r;
22306                 if(k == e.TAB){
22307                     e.stopEvent();
22308                     r = this.doc.selection.createRange();
22309                     if(r){
22310                         r.collapse(true);
22311                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22312                         this.deferFocus();
22313                     }
22314                     return;
22315                 }
22316                 
22317                 if(k == e.ENTER){
22318                     r = this.doc.selection.createRange();
22319                     if(r){
22320                         var target = r.parentElement();
22321                         if(!target || target.tagName.toLowerCase() != 'li'){
22322                             e.stopEvent();
22323                             r.pasteHTML('<br />');
22324                             r.collapse(false);
22325                             r.select();
22326                         }
22327                     }
22328                 }
22329                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22330                     this.cleanUpPaste.defer(100, this);
22331                     return;
22332                 }
22333                 
22334                 
22335             };
22336         }else if(Roo.isOpera){
22337             return function(e){
22338                 var k = e.getKey();
22339                 if(k == e.TAB){
22340                     e.stopEvent();
22341                     this.win.focus();
22342                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22343                     this.deferFocus();
22344                 }
22345                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22346                     this.cleanUpPaste.defer(100, this);
22347                     return;
22348                 }
22349                 
22350             };
22351         }else if(Roo.isSafari){
22352             return function(e){
22353                 var k = e.getKey();
22354                 
22355                 if(k == e.TAB){
22356                     e.stopEvent();
22357                     this.execCmd('InsertText','\t');
22358                     this.deferFocus();
22359                     return;
22360                 }
22361                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22362                     this.cleanUpPaste.defer(100, this);
22363                     return;
22364                 }
22365                 
22366              };
22367         }
22368     }(),
22369     
22370     getAllAncestors: function()
22371     {
22372         var p = this.getSelectedNode();
22373         var a = [];
22374         if (!p) {
22375             a.push(p); // push blank onto stack..
22376             p = this.getParentElement();
22377         }
22378         
22379         
22380         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22381             a.push(p);
22382             p = p.parentNode;
22383         }
22384         a.push(this.doc.body);
22385         return a;
22386     },
22387     lastSel : false,
22388     lastSelNode : false,
22389     
22390     
22391     getSelection : function() 
22392     {
22393         this.assignDocWin();
22394         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22395     },
22396     
22397     getSelectedNode: function() 
22398     {
22399         // this may only work on Gecko!!!
22400         
22401         // should we cache this!!!!
22402         
22403         
22404         
22405          
22406         var range = this.createRange(this.getSelection()).cloneRange();
22407         
22408         if (Roo.isIE) {
22409             var parent = range.parentElement();
22410             while (true) {
22411                 var testRange = range.duplicate();
22412                 testRange.moveToElementText(parent);
22413                 if (testRange.inRange(range)) {
22414                     break;
22415                 }
22416                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22417                     break;
22418                 }
22419                 parent = parent.parentElement;
22420             }
22421             return parent;
22422         }
22423         
22424         // is ancestor a text element.
22425         var ac =  range.commonAncestorContainer;
22426         if (ac.nodeType == 3) {
22427             ac = ac.parentNode;
22428         }
22429         
22430         var ar = ac.childNodes;
22431          
22432         var nodes = [];
22433         var other_nodes = [];
22434         var has_other_nodes = false;
22435         for (var i=0;i<ar.length;i++) {
22436             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22437                 continue;
22438             }
22439             // fullly contained node.
22440             
22441             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22442                 nodes.push(ar[i]);
22443                 continue;
22444             }
22445             
22446             // probably selected..
22447             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22448                 other_nodes.push(ar[i]);
22449                 continue;
22450             }
22451             // outer..
22452             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22453                 continue;
22454             }
22455             
22456             
22457             has_other_nodes = true;
22458         }
22459         if (!nodes.length && other_nodes.length) {
22460             nodes= other_nodes;
22461         }
22462         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22463             return false;
22464         }
22465         
22466         return nodes[0];
22467     },
22468     createRange: function(sel)
22469     {
22470         // this has strange effects when using with 
22471         // top toolbar - not sure if it's a great idea.
22472         //this.editor.contentWindow.focus();
22473         if (typeof sel != "undefined") {
22474             try {
22475                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22476             } catch(e) {
22477                 return this.doc.createRange();
22478             }
22479         } else {
22480             return this.doc.createRange();
22481         }
22482     },
22483     getParentElement: function()
22484     {
22485         
22486         this.assignDocWin();
22487         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22488         
22489         var range = this.createRange(sel);
22490          
22491         try {
22492             var p = range.commonAncestorContainer;
22493             while (p.nodeType == 3) { // text node
22494                 p = p.parentNode;
22495             }
22496             return p;
22497         } catch (e) {
22498             return null;
22499         }
22500     
22501     },
22502     /***
22503      *
22504      * Range intersection.. the hard stuff...
22505      *  '-1' = before
22506      *  '0' = hits..
22507      *  '1' = after.
22508      *         [ -- selected range --- ]
22509      *   [fail]                        [fail]
22510      *
22511      *    basically..
22512      *      if end is before start or  hits it. fail.
22513      *      if start is after end or hits it fail.
22514      *
22515      *   if either hits (but other is outside. - then it's not 
22516      *   
22517      *    
22518      **/
22519     
22520     
22521     // @see http://www.thismuchiknow.co.uk/?p=64.
22522     rangeIntersectsNode : function(range, node)
22523     {
22524         var nodeRange = node.ownerDocument.createRange();
22525         try {
22526             nodeRange.selectNode(node);
22527         } catch (e) {
22528             nodeRange.selectNodeContents(node);
22529         }
22530     
22531         var rangeStartRange = range.cloneRange();
22532         rangeStartRange.collapse(true);
22533     
22534         var rangeEndRange = range.cloneRange();
22535         rangeEndRange.collapse(false);
22536     
22537         var nodeStartRange = nodeRange.cloneRange();
22538         nodeStartRange.collapse(true);
22539     
22540         var nodeEndRange = nodeRange.cloneRange();
22541         nodeEndRange.collapse(false);
22542     
22543         return rangeStartRange.compareBoundaryPoints(
22544                  Range.START_TO_START, nodeEndRange) == -1 &&
22545                rangeEndRange.compareBoundaryPoints(
22546                  Range.START_TO_START, nodeStartRange) == 1;
22547         
22548          
22549     },
22550     rangeCompareNode : function(range, node)
22551     {
22552         var nodeRange = node.ownerDocument.createRange();
22553         try {
22554             nodeRange.selectNode(node);
22555         } catch (e) {
22556             nodeRange.selectNodeContents(node);
22557         }
22558         
22559         
22560         range.collapse(true);
22561     
22562         nodeRange.collapse(true);
22563      
22564         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22565         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22566          
22567         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22568         
22569         var nodeIsBefore   =  ss == 1;
22570         var nodeIsAfter    = ee == -1;
22571         
22572         if (nodeIsBefore && nodeIsAfter) {
22573             return 0; // outer
22574         }
22575         if (!nodeIsBefore && nodeIsAfter) {
22576             return 1; //right trailed.
22577         }
22578         
22579         if (nodeIsBefore && !nodeIsAfter) {
22580             return 2;  // left trailed.
22581         }
22582         // fully contined.
22583         return 3;
22584     },
22585
22586     // private? - in a new class?
22587     cleanUpPaste :  function()
22588     {
22589         // cleans up the whole document..
22590         Roo.log('cleanuppaste');
22591         
22592         this.cleanUpChildren(this.doc.body);
22593         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22594         if (clean != this.doc.body.innerHTML) {
22595             this.doc.body.innerHTML = clean;
22596         }
22597         
22598     },
22599     
22600     cleanWordChars : function(input) {// change the chars to hex code
22601         var he = Roo.HtmlEditorCore;
22602         
22603         var output = input;
22604         Roo.each(he.swapCodes, function(sw) { 
22605             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22606             
22607             output = output.replace(swapper, sw[1]);
22608         });
22609         
22610         return output;
22611     },
22612     
22613     
22614     cleanUpChildren : function (n)
22615     {
22616         if (!n.childNodes.length) {
22617             return;
22618         }
22619         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22620            this.cleanUpChild(n.childNodes[i]);
22621         }
22622     },
22623     
22624     
22625         
22626     
22627     cleanUpChild : function (node)
22628     {
22629         var ed = this;
22630         //console.log(node);
22631         if (node.nodeName == "#text") {
22632             // clean up silly Windows -- stuff?
22633             return; 
22634         }
22635         if (node.nodeName == "#comment") {
22636             node.parentNode.removeChild(node);
22637             // clean up silly Windows -- stuff?
22638             return; 
22639         }
22640         var lcname = node.tagName.toLowerCase();
22641         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22642         // whitelist of tags..
22643         
22644         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22645             // remove node.
22646             node.parentNode.removeChild(node);
22647             return;
22648             
22649         }
22650         
22651         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22652         
22653         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22654         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22655         
22656         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22657         //    remove_keep_children = true;
22658         //}
22659         
22660         if (remove_keep_children) {
22661             this.cleanUpChildren(node);
22662             // inserts everything just before this node...
22663             while (node.childNodes.length) {
22664                 var cn = node.childNodes[0];
22665                 node.removeChild(cn);
22666                 node.parentNode.insertBefore(cn, node);
22667             }
22668             node.parentNode.removeChild(node);
22669             return;
22670         }
22671         
22672         if (!node.attributes || !node.attributes.length) {
22673             this.cleanUpChildren(node);
22674             return;
22675         }
22676         
22677         function cleanAttr(n,v)
22678         {
22679             
22680             if (v.match(/^\./) || v.match(/^\//)) {
22681                 return;
22682             }
22683             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22684                 return;
22685             }
22686             if (v.match(/^#/)) {
22687                 return;
22688             }
22689 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22690             node.removeAttribute(n);
22691             
22692         }
22693         
22694         var cwhite = this.cwhite;
22695         var cblack = this.cblack;
22696             
22697         function cleanStyle(n,v)
22698         {
22699             if (v.match(/expression/)) { //XSS?? should we even bother..
22700                 node.removeAttribute(n);
22701                 return;
22702             }
22703             
22704             var parts = v.split(/;/);
22705             var clean = [];
22706             
22707             Roo.each(parts, function(p) {
22708                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22709                 if (!p.length) {
22710                     return true;
22711                 }
22712                 var l = p.split(':').shift().replace(/\s+/g,'');
22713                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22714                 
22715                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22716 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22717                     //node.removeAttribute(n);
22718                     return true;
22719                 }
22720                 //Roo.log()
22721                 // only allow 'c whitelisted system attributes'
22722                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22723 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22724                     //node.removeAttribute(n);
22725                     return true;
22726                 }
22727                 
22728                 
22729                  
22730                 
22731                 clean.push(p);
22732                 return true;
22733             });
22734             if (clean.length) { 
22735                 node.setAttribute(n, clean.join(';'));
22736             } else {
22737                 node.removeAttribute(n);
22738             }
22739             
22740         }
22741         
22742         
22743         for (var i = node.attributes.length-1; i > -1 ; i--) {
22744             var a = node.attributes[i];
22745             //console.log(a);
22746             
22747             if (a.name.toLowerCase().substr(0,2)=='on')  {
22748                 node.removeAttribute(a.name);
22749                 continue;
22750             }
22751             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22752                 node.removeAttribute(a.name);
22753                 continue;
22754             }
22755             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22756                 cleanAttr(a.name,a.value); // fixme..
22757                 continue;
22758             }
22759             if (a.name == 'style') {
22760                 cleanStyle(a.name,a.value);
22761                 continue;
22762             }
22763             /// clean up MS crap..
22764             // tecnically this should be a list of valid class'es..
22765             
22766             
22767             if (a.name == 'class') {
22768                 if (a.value.match(/^Mso/)) {
22769                     node.className = '';
22770                 }
22771                 
22772                 if (a.value.match(/^body$/)) {
22773                     node.className = '';
22774                 }
22775                 continue;
22776             }
22777             
22778             // style cleanup!?
22779             // class cleanup?
22780             
22781         }
22782         
22783         
22784         this.cleanUpChildren(node);
22785         
22786         
22787     },
22788     
22789     /**
22790      * Clean up MS wordisms...
22791      */
22792     cleanWord : function(node)
22793     {
22794         
22795         
22796         if (!node) {
22797             this.cleanWord(this.doc.body);
22798             return;
22799         }
22800         if (node.nodeName == "#text") {
22801             // clean up silly Windows -- stuff?
22802             return; 
22803         }
22804         if (node.nodeName == "#comment") {
22805             node.parentNode.removeChild(node);
22806             // clean up silly Windows -- stuff?
22807             return; 
22808         }
22809         
22810         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22811             node.parentNode.removeChild(node);
22812             return;
22813         }
22814         
22815         // remove - but keep children..
22816         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22817             while (node.childNodes.length) {
22818                 var cn = node.childNodes[0];
22819                 node.removeChild(cn);
22820                 node.parentNode.insertBefore(cn, node);
22821             }
22822             node.parentNode.removeChild(node);
22823             this.iterateChildren(node, this.cleanWord);
22824             return;
22825         }
22826         // clean styles
22827         if (node.className.length) {
22828             
22829             var cn = node.className.split(/\W+/);
22830             var cna = [];
22831             Roo.each(cn, function(cls) {
22832                 if (cls.match(/Mso[a-zA-Z]+/)) {
22833                     return;
22834                 }
22835                 cna.push(cls);
22836             });
22837             node.className = cna.length ? cna.join(' ') : '';
22838             if (!cna.length) {
22839                 node.removeAttribute("class");
22840             }
22841         }
22842         
22843         if (node.hasAttribute("lang")) {
22844             node.removeAttribute("lang");
22845         }
22846         
22847         if (node.hasAttribute("style")) {
22848             
22849             var styles = node.getAttribute("style").split(";");
22850             var nstyle = [];
22851             Roo.each(styles, function(s) {
22852                 if (!s.match(/:/)) {
22853                     return;
22854                 }
22855                 var kv = s.split(":");
22856                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22857                     return;
22858                 }
22859                 // what ever is left... we allow.
22860                 nstyle.push(s);
22861             });
22862             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22863             if (!nstyle.length) {
22864                 node.removeAttribute('style');
22865             }
22866         }
22867         this.iterateChildren(node, this.cleanWord);
22868         
22869         
22870         
22871     },
22872     /**
22873      * iterateChildren of a Node, calling fn each time, using this as the scole..
22874      * @param {DomNode} node node to iterate children of.
22875      * @param {Function} fn method of this class to call on each item.
22876      */
22877     iterateChildren : function(node, fn)
22878     {
22879         if (!node.childNodes.length) {
22880                 return;
22881         }
22882         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22883            fn.call(this, node.childNodes[i])
22884         }
22885     },
22886     
22887     
22888     /**
22889      * cleanTableWidths.
22890      *
22891      * Quite often pasting from word etc.. results in tables with column and widths.
22892      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22893      *
22894      */
22895     cleanTableWidths : function(node)
22896     {
22897          
22898          
22899         if (!node) {
22900             this.cleanTableWidths(this.doc.body);
22901             return;
22902         }
22903         
22904         // ignore list...
22905         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22906             return; 
22907         }
22908         Roo.log(node.tagName);
22909         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22910             this.iterateChildren(node, this.cleanTableWidths);
22911             return;
22912         }
22913         if (node.hasAttribute('width')) {
22914             node.removeAttribute('width');
22915         }
22916         
22917          
22918         if (node.hasAttribute("style")) {
22919             // pretty basic...
22920             
22921             var styles = node.getAttribute("style").split(";");
22922             var nstyle = [];
22923             Roo.each(styles, function(s) {
22924                 if (!s.match(/:/)) {
22925                     return;
22926                 }
22927                 var kv = s.split(":");
22928                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22929                     return;
22930                 }
22931                 // what ever is left... we allow.
22932                 nstyle.push(s);
22933             });
22934             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22935             if (!nstyle.length) {
22936                 node.removeAttribute('style');
22937             }
22938         }
22939         
22940         this.iterateChildren(node, this.cleanTableWidths);
22941         
22942         
22943     },
22944     
22945     
22946     
22947     
22948     domToHTML : function(currentElement, depth, nopadtext) {
22949         
22950         depth = depth || 0;
22951         nopadtext = nopadtext || false;
22952     
22953         if (!currentElement) {
22954             return this.domToHTML(this.doc.body);
22955         }
22956         
22957         //Roo.log(currentElement);
22958         var j;
22959         var allText = false;
22960         var nodeName = currentElement.nodeName;
22961         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22962         
22963         if  (nodeName == '#text') {
22964             
22965             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22966         }
22967         
22968         
22969         var ret = '';
22970         if (nodeName != 'BODY') {
22971              
22972             var i = 0;
22973             // Prints the node tagName, such as <A>, <IMG>, etc
22974             if (tagName) {
22975                 var attr = [];
22976                 for(i = 0; i < currentElement.attributes.length;i++) {
22977                     // quoting?
22978                     var aname = currentElement.attributes.item(i).name;
22979                     if (!currentElement.attributes.item(i).value.length) {
22980                         continue;
22981                     }
22982                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22983                 }
22984                 
22985                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22986             } 
22987             else {
22988                 
22989                 // eack
22990             }
22991         } else {
22992             tagName = false;
22993         }
22994         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22995             return ret;
22996         }
22997         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22998             nopadtext = true;
22999         }
23000         
23001         
23002         // Traverse the tree
23003         i = 0;
23004         var currentElementChild = currentElement.childNodes.item(i);
23005         var allText = true;
23006         var innerHTML  = '';
23007         lastnode = '';
23008         while (currentElementChild) {
23009             // Formatting code (indent the tree so it looks nice on the screen)
23010             var nopad = nopadtext;
23011             if (lastnode == 'SPAN') {
23012                 nopad  = true;
23013             }
23014             // text
23015             if  (currentElementChild.nodeName == '#text') {
23016                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23017                 toadd = nopadtext ? toadd : toadd.trim();
23018                 if (!nopad && toadd.length > 80) {
23019                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23020                 }
23021                 innerHTML  += toadd;
23022                 
23023                 i++;
23024                 currentElementChild = currentElement.childNodes.item(i);
23025                 lastNode = '';
23026                 continue;
23027             }
23028             allText = false;
23029             
23030             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23031                 
23032             // Recursively traverse the tree structure of the child node
23033             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23034             lastnode = currentElementChild.nodeName;
23035             i++;
23036             currentElementChild=currentElement.childNodes.item(i);
23037         }
23038         
23039         ret += innerHTML;
23040         
23041         if (!allText) {
23042                 // The remaining code is mostly for formatting the tree
23043             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23044         }
23045         
23046         
23047         if (tagName) {
23048             ret+= "</"+tagName+">";
23049         }
23050         return ret;
23051         
23052     },
23053         
23054     applyBlacklists : function()
23055     {
23056         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23057         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23058         
23059         this.white = [];
23060         this.black = [];
23061         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23062             if (b.indexOf(tag) > -1) {
23063                 return;
23064             }
23065             this.white.push(tag);
23066             
23067         }, this);
23068         
23069         Roo.each(w, function(tag) {
23070             if (b.indexOf(tag) > -1) {
23071                 return;
23072             }
23073             if (this.white.indexOf(tag) > -1) {
23074                 return;
23075             }
23076             this.white.push(tag);
23077             
23078         }, this);
23079         
23080         
23081         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23082             if (w.indexOf(tag) > -1) {
23083                 return;
23084             }
23085             this.black.push(tag);
23086             
23087         }, this);
23088         
23089         Roo.each(b, function(tag) {
23090             if (w.indexOf(tag) > -1) {
23091                 return;
23092             }
23093             if (this.black.indexOf(tag) > -1) {
23094                 return;
23095             }
23096             this.black.push(tag);
23097             
23098         }, this);
23099         
23100         
23101         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23102         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23103         
23104         this.cwhite = [];
23105         this.cblack = [];
23106         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23107             if (b.indexOf(tag) > -1) {
23108                 return;
23109             }
23110             this.cwhite.push(tag);
23111             
23112         }, this);
23113         
23114         Roo.each(w, function(tag) {
23115             if (b.indexOf(tag) > -1) {
23116                 return;
23117             }
23118             if (this.cwhite.indexOf(tag) > -1) {
23119                 return;
23120             }
23121             this.cwhite.push(tag);
23122             
23123         }, this);
23124         
23125         
23126         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23127             if (w.indexOf(tag) > -1) {
23128                 return;
23129             }
23130             this.cblack.push(tag);
23131             
23132         }, this);
23133         
23134         Roo.each(b, function(tag) {
23135             if (w.indexOf(tag) > -1) {
23136                 return;
23137             }
23138             if (this.cblack.indexOf(tag) > -1) {
23139                 return;
23140             }
23141             this.cblack.push(tag);
23142             
23143         }, this);
23144     },
23145     
23146     setStylesheets : function(stylesheets)
23147     {
23148         if(typeof(stylesheets) == 'string'){
23149             Roo.get(this.iframe.contentDocument.head).createChild({
23150                 tag : 'link',
23151                 rel : 'stylesheet',
23152                 type : 'text/css',
23153                 href : stylesheets
23154             });
23155             
23156             return;
23157         }
23158         var _this = this;
23159      
23160         Roo.each(stylesheets, function(s) {
23161             if(!s.length){
23162                 return;
23163             }
23164             
23165             Roo.get(_this.iframe.contentDocument.head).createChild({
23166                 tag : 'link',
23167                 rel : 'stylesheet',
23168                 type : 'text/css',
23169                 href : s
23170             });
23171         });
23172
23173         
23174     },
23175     
23176     removeStylesheets : function()
23177     {
23178         var _this = this;
23179         
23180         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23181             s.remove();
23182         });
23183     },
23184     
23185     setStyle : function(style)
23186     {
23187         Roo.get(this.iframe.contentDocument.head).createChild({
23188             tag : 'style',
23189             type : 'text/css',
23190             html : style
23191         });
23192
23193         return;
23194     }
23195     
23196     // hide stuff that is not compatible
23197     /**
23198      * @event blur
23199      * @hide
23200      */
23201     /**
23202      * @event change
23203      * @hide
23204      */
23205     /**
23206      * @event focus
23207      * @hide
23208      */
23209     /**
23210      * @event specialkey
23211      * @hide
23212      */
23213     /**
23214      * @cfg {String} fieldClass @hide
23215      */
23216     /**
23217      * @cfg {String} focusClass @hide
23218      */
23219     /**
23220      * @cfg {String} autoCreate @hide
23221      */
23222     /**
23223      * @cfg {String} inputType @hide
23224      */
23225     /**
23226      * @cfg {String} invalidClass @hide
23227      */
23228     /**
23229      * @cfg {String} invalidText @hide
23230      */
23231     /**
23232      * @cfg {String} msgFx @hide
23233      */
23234     /**
23235      * @cfg {String} validateOnBlur @hide
23236      */
23237 });
23238
23239 Roo.HtmlEditorCore.white = [
23240         'area', 'br', 'img', 'input', 'hr', 'wbr',
23241         
23242        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23243        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23244        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23245        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23246        'table',   'ul',         'xmp', 
23247        
23248        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23249       'thead',   'tr', 
23250      
23251       'dir', 'menu', 'ol', 'ul', 'dl',
23252        
23253       'embed',  'object'
23254 ];
23255
23256
23257 Roo.HtmlEditorCore.black = [
23258     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23259         'applet', // 
23260         'base',   'basefont', 'bgsound', 'blink',  'body', 
23261         'frame',  'frameset', 'head',    'html',   'ilayer', 
23262         'iframe', 'layer',  'link',     'meta',    'object',   
23263         'script', 'style' ,'title',  'xml' // clean later..
23264 ];
23265 Roo.HtmlEditorCore.clean = [
23266     'script', 'style', 'title', 'xml'
23267 ];
23268 Roo.HtmlEditorCore.remove = [
23269     'font'
23270 ];
23271 // attributes..
23272
23273 Roo.HtmlEditorCore.ablack = [
23274     'on'
23275 ];
23276     
23277 Roo.HtmlEditorCore.aclean = [ 
23278     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23279 ];
23280
23281 // protocols..
23282 Roo.HtmlEditorCore.pwhite= [
23283         'http',  'https',  'mailto'
23284 ];
23285
23286 // white listed style attributes.
23287 Roo.HtmlEditorCore.cwhite= [
23288       //  'text-align', /// default is to allow most things..
23289       
23290          
23291 //        'font-size'//??
23292 ];
23293
23294 // black listed style attributes.
23295 Roo.HtmlEditorCore.cblack= [
23296       //  'font-size' -- this can be set by the project 
23297 ];
23298
23299
23300 Roo.HtmlEditorCore.swapCodes   =[ 
23301     [    8211, "--" ], 
23302     [    8212, "--" ], 
23303     [    8216,  "'" ],  
23304     [    8217, "'" ],  
23305     [    8220, '"' ],  
23306     [    8221, '"' ],  
23307     [    8226, "*" ],  
23308     [    8230, "..." ]
23309 ]; 
23310
23311     /*
23312  * - LGPL
23313  *
23314  * HtmlEditor
23315  * 
23316  */
23317
23318 /**
23319  * @class Roo.bootstrap.HtmlEditor
23320  * @extends Roo.bootstrap.TextArea
23321  * Bootstrap HtmlEditor class
23322
23323  * @constructor
23324  * Create a new HtmlEditor
23325  * @param {Object} config The config object
23326  */
23327
23328 Roo.bootstrap.HtmlEditor = function(config){
23329     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23330     if (!this.toolbars) {
23331         this.toolbars = [];
23332     }
23333     
23334     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23335     this.addEvents({
23336             /**
23337              * @event initialize
23338              * Fires when the editor is fully initialized (including the iframe)
23339              * @param {HtmlEditor} this
23340              */
23341             initialize: true,
23342             /**
23343              * @event activate
23344              * Fires when the editor is first receives the focus. Any insertion must wait
23345              * until after this event.
23346              * @param {HtmlEditor} this
23347              */
23348             activate: true,
23349              /**
23350              * @event beforesync
23351              * Fires before the textarea is updated with content from the editor iframe. Return false
23352              * to cancel the sync.
23353              * @param {HtmlEditor} this
23354              * @param {String} html
23355              */
23356             beforesync: true,
23357              /**
23358              * @event beforepush
23359              * Fires before the iframe editor is updated with content from the textarea. Return false
23360              * to cancel the push.
23361              * @param {HtmlEditor} this
23362              * @param {String} html
23363              */
23364             beforepush: true,
23365              /**
23366              * @event sync
23367              * Fires when the textarea is updated with content from the editor iframe.
23368              * @param {HtmlEditor} this
23369              * @param {String} html
23370              */
23371             sync: true,
23372              /**
23373              * @event push
23374              * Fires when the iframe editor is updated with content from the textarea.
23375              * @param {HtmlEditor} this
23376              * @param {String} html
23377              */
23378             push: true,
23379              /**
23380              * @event editmodechange
23381              * Fires when the editor switches edit modes
23382              * @param {HtmlEditor} this
23383              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23384              */
23385             editmodechange: true,
23386             /**
23387              * @event editorevent
23388              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23389              * @param {HtmlEditor} this
23390              */
23391             editorevent: true,
23392             /**
23393              * @event firstfocus
23394              * Fires when on first focus - needed by toolbars..
23395              * @param {HtmlEditor} this
23396              */
23397             firstfocus: true,
23398             /**
23399              * @event autosave
23400              * Auto save the htmlEditor value as a file into Events
23401              * @param {HtmlEditor} this
23402              */
23403             autosave: true,
23404             /**
23405              * @event savedpreview
23406              * preview the saved version of htmlEditor
23407              * @param {HtmlEditor} this
23408              */
23409             savedpreview: true
23410         });
23411 };
23412
23413
23414 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23415     
23416     
23417       /**
23418      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23419      */
23420     toolbars : false,
23421     
23422      /**
23423     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23424     */
23425     btns : [],
23426    
23427      /**
23428      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23429      *                        Roo.resizable.
23430      */
23431     resizable : false,
23432      /**
23433      * @cfg {Number} height (in pixels)
23434      */   
23435     height: 300,
23436    /**
23437      * @cfg {Number} width (in pixels)
23438      */   
23439     width: false,
23440     
23441     /**
23442      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23443      * 
23444      */
23445     stylesheets: false,
23446     
23447     // id of frame..
23448     frameId: false,
23449     
23450     // private properties
23451     validationEvent : false,
23452     deferHeight: true,
23453     initialized : false,
23454     activated : false,
23455     
23456     onFocus : Roo.emptyFn,
23457     iframePad:3,
23458     hideMode:'offsets',
23459     
23460     tbContainer : false,
23461     
23462     bodyCls : '',
23463     
23464     toolbarContainer :function() {
23465         return this.wrap.select('.x-html-editor-tb',true).first();
23466     },
23467
23468     /**
23469      * Protected method that will not generally be called directly. It
23470      * is called when the editor creates its toolbar. Override this method if you need to
23471      * add custom toolbar buttons.
23472      * @param {HtmlEditor} editor
23473      */
23474     createToolbar : function(){
23475         Roo.log('renewing');
23476         Roo.log("create toolbars");
23477         
23478         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23479         this.toolbars[0].render(this.toolbarContainer());
23480         
23481         return;
23482         
23483 //        if (!editor.toolbars || !editor.toolbars.length) {
23484 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23485 //        }
23486 //        
23487 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23488 //            editor.toolbars[i] = Roo.factory(
23489 //                    typeof(editor.toolbars[i]) == 'string' ?
23490 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23491 //                Roo.bootstrap.HtmlEditor);
23492 //            editor.toolbars[i].init(editor);
23493 //        }
23494     },
23495
23496      
23497     // private
23498     onRender : function(ct, position)
23499     {
23500        // Roo.log("Call onRender: " + this.xtype);
23501         var _t = this;
23502         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23503       
23504         this.wrap = this.inputEl().wrap({
23505             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23506         });
23507         
23508         this.editorcore.onRender(ct, position);
23509          
23510         if (this.resizable) {
23511             this.resizeEl = new Roo.Resizable(this.wrap, {
23512                 pinned : true,
23513                 wrap: true,
23514                 dynamic : true,
23515                 minHeight : this.height,
23516                 height: this.height,
23517                 handles : this.resizable,
23518                 width: this.width,
23519                 listeners : {
23520                     resize : function(r, w, h) {
23521                         _t.onResize(w,h); // -something
23522                     }
23523                 }
23524             });
23525             
23526         }
23527         this.createToolbar(this);
23528        
23529         
23530         if(!this.width && this.resizable){
23531             this.setSize(this.wrap.getSize());
23532         }
23533         if (this.resizeEl) {
23534             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23535             // should trigger onReize..
23536         }
23537         
23538     },
23539
23540     // private
23541     onResize : function(w, h)
23542     {
23543         Roo.log('resize: ' +w + ',' + h );
23544         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23545         var ew = false;
23546         var eh = false;
23547         
23548         if(this.inputEl() ){
23549             if(typeof w == 'number'){
23550                 var aw = w - this.wrap.getFrameWidth('lr');
23551                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23552                 ew = aw;
23553             }
23554             if(typeof h == 'number'){
23555                  var tbh = -11;  // fixme it needs to tool bar size!
23556                 for (var i =0; i < this.toolbars.length;i++) {
23557                     // fixme - ask toolbars for heights?
23558                     tbh += this.toolbars[i].el.getHeight();
23559                     //if (this.toolbars[i].footer) {
23560                     //    tbh += this.toolbars[i].footer.el.getHeight();
23561                     //}
23562                 }
23563               
23564                 
23565                 
23566                 
23567                 
23568                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23569                 ah -= 5; // knock a few pixes off for look..
23570                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23571                 var eh = ah;
23572             }
23573         }
23574         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23575         this.editorcore.onResize(ew,eh);
23576         
23577     },
23578
23579     /**
23580      * Toggles the editor between standard and source edit mode.
23581      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23582      */
23583     toggleSourceEdit : function(sourceEditMode)
23584     {
23585         this.editorcore.toggleSourceEdit(sourceEditMode);
23586         
23587         if(this.editorcore.sourceEditMode){
23588             Roo.log('editor - showing textarea');
23589             
23590 //            Roo.log('in');
23591 //            Roo.log(this.syncValue());
23592             this.syncValue();
23593             this.inputEl().removeClass(['hide', 'x-hidden']);
23594             this.inputEl().dom.removeAttribute('tabIndex');
23595             this.inputEl().focus();
23596         }else{
23597             Roo.log('editor - hiding textarea');
23598 //            Roo.log('out')
23599 //            Roo.log(this.pushValue()); 
23600             this.pushValue();
23601             
23602             this.inputEl().addClass(['hide', 'x-hidden']);
23603             this.inputEl().dom.setAttribute('tabIndex', -1);
23604             //this.deferFocus();
23605         }
23606          
23607         if(this.resizable){
23608             this.setSize(this.wrap.getSize());
23609         }
23610         
23611         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23612     },
23613  
23614     // private (for BoxComponent)
23615     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23616
23617     // private (for BoxComponent)
23618     getResizeEl : function(){
23619         return this.wrap;
23620     },
23621
23622     // private (for BoxComponent)
23623     getPositionEl : function(){
23624         return this.wrap;
23625     },
23626
23627     // private
23628     initEvents : function(){
23629         this.originalValue = this.getValue();
23630     },
23631
23632 //    /**
23633 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23634 //     * @method
23635 //     */
23636 //    markInvalid : Roo.emptyFn,
23637 //    /**
23638 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23639 //     * @method
23640 //     */
23641 //    clearInvalid : Roo.emptyFn,
23642
23643     setValue : function(v){
23644         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23645         this.editorcore.pushValue();
23646     },
23647
23648      
23649     // private
23650     deferFocus : function(){
23651         this.focus.defer(10, this);
23652     },
23653
23654     // doc'ed in Field
23655     focus : function(){
23656         this.editorcore.focus();
23657         
23658     },
23659       
23660
23661     // private
23662     onDestroy : function(){
23663         
23664         
23665         
23666         if(this.rendered){
23667             
23668             for (var i =0; i < this.toolbars.length;i++) {
23669                 // fixme - ask toolbars for heights?
23670                 this.toolbars[i].onDestroy();
23671             }
23672             
23673             this.wrap.dom.innerHTML = '';
23674             this.wrap.remove();
23675         }
23676     },
23677
23678     // private
23679     onFirstFocus : function(){
23680         //Roo.log("onFirstFocus");
23681         this.editorcore.onFirstFocus();
23682          for (var i =0; i < this.toolbars.length;i++) {
23683             this.toolbars[i].onFirstFocus();
23684         }
23685         
23686     },
23687     
23688     // private
23689     syncValue : function()
23690     {   
23691         this.editorcore.syncValue();
23692     },
23693     
23694     pushValue : function()
23695     {   
23696         this.editorcore.pushValue();
23697     }
23698      
23699     
23700     // hide stuff that is not compatible
23701     /**
23702      * @event blur
23703      * @hide
23704      */
23705     /**
23706      * @event change
23707      * @hide
23708      */
23709     /**
23710      * @event focus
23711      * @hide
23712      */
23713     /**
23714      * @event specialkey
23715      * @hide
23716      */
23717     /**
23718      * @cfg {String} fieldClass @hide
23719      */
23720     /**
23721      * @cfg {String} focusClass @hide
23722      */
23723     /**
23724      * @cfg {String} autoCreate @hide
23725      */
23726     /**
23727      * @cfg {String} inputType @hide
23728      */
23729     /**
23730      * @cfg {String} invalidClass @hide
23731      */
23732     /**
23733      * @cfg {String} invalidText @hide
23734      */
23735     /**
23736      * @cfg {String} msgFx @hide
23737      */
23738     /**
23739      * @cfg {String} validateOnBlur @hide
23740      */
23741 });
23742  
23743     
23744    
23745    
23746    
23747       
23748 Roo.namespace('Roo.bootstrap.htmleditor');
23749 /**
23750  * @class Roo.bootstrap.HtmlEditorToolbar1
23751  * Basic Toolbar
23752  * 
23753  * Usage:
23754  *
23755  new Roo.bootstrap.HtmlEditor({
23756     ....
23757     toolbars : [
23758         new Roo.bootstrap.HtmlEditorToolbar1({
23759             disable : { fonts: 1 , format: 1, ..., ... , ...],
23760             btns : [ .... ]
23761         })
23762     }
23763      
23764  * 
23765  * @cfg {Object} disable List of elements to disable..
23766  * @cfg {Array} btns List of additional buttons.
23767  * 
23768  * 
23769  * NEEDS Extra CSS? 
23770  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23771  */
23772  
23773 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23774 {
23775     
23776     Roo.apply(this, config);
23777     
23778     // default disabled, based on 'good practice'..
23779     this.disable = this.disable || {};
23780     Roo.applyIf(this.disable, {
23781         fontSize : true,
23782         colors : true,
23783         specialElements : true
23784     });
23785     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23786     
23787     this.editor = config.editor;
23788     this.editorcore = config.editor.editorcore;
23789     
23790     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23791     
23792     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23793     // dont call parent... till later.
23794 }
23795 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23796      
23797     bar : true,
23798     
23799     editor : false,
23800     editorcore : false,
23801     
23802     
23803     formats : [
23804         "p" ,  
23805         "h1","h2","h3","h4","h5","h6", 
23806         "pre", "code", 
23807         "abbr", "acronym", "address", "cite", "samp", "var",
23808         'div','span'
23809     ],
23810     
23811     onRender : function(ct, position)
23812     {
23813        // Roo.log("Call onRender: " + this.xtype);
23814         
23815        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23816        Roo.log(this.el);
23817        this.el.dom.style.marginBottom = '0';
23818        var _this = this;
23819        var editorcore = this.editorcore;
23820        var editor= this.editor;
23821        
23822        var children = [];
23823        var btn = function(id,cmd , toggle, handler, html){
23824        
23825             var  event = toggle ? 'toggle' : 'click';
23826        
23827             var a = {
23828                 size : 'sm',
23829                 xtype: 'Button',
23830                 xns: Roo.bootstrap,
23831                 glyphicon : id,
23832                 cmd : id || cmd,
23833                 enableToggle:toggle !== false,
23834                 html : html || '',
23835                 pressed : toggle ? false : null,
23836                 listeners : {}
23837             };
23838             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23839                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23840             };
23841             children.push(a);
23842             return a;
23843        }
23844        
23845     //    var cb_box = function...
23846         
23847         var style = {
23848                 xtype: 'Button',
23849                 size : 'sm',
23850                 xns: Roo.bootstrap,
23851                 glyphicon : 'font',
23852                 //html : 'submit'
23853                 menu : {
23854                     xtype: 'Menu',
23855                     xns: Roo.bootstrap,
23856                     items:  []
23857                 }
23858         };
23859         Roo.each(this.formats, function(f) {
23860             style.menu.items.push({
23861                 xtype :'MenuItem',
23862                 xns: Roo.bootstrap,
23863                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23864                 tagname : f,
23865                 listeners : {
23866                     click : function()
23867                     {
23868                         editorcore.insertTag(this.tagname);
23869                         editor.focus();
23870                     }
23871                 }
23872                 
23873             });
23874         });
23875         children.push(style);   
23876         
23877         btn('bold',false,true);
23878         btn('italic',false,true);
23879         btn('align-left', 'justifyleft',true);
23880         btn('align-center', 'justifycenter',true);
23881         btn('align-right' , 'justifyright',true);
23882         btn('link', false, false, function(btn) {
23883             //Roo.log("create link?");
23884             var url = prompt(this.createLinkText, this.defaultLinkValue);
23885             if(url && url != 'http:/'+'/'){
23886                 this.editorcore.relayCmd('createlink', url);
23887             }
23888         }),
23889         btn('list','insertunorderedlist',true);
23890         btn('pencil', false,true, function(btn){
23891                 Roo.log(this);
23892                 this.toggleSourceEdit(btn.pressed);
23893         });
23894         
23895         if (this.editor.btns.length > 0) {
23896             for (var i = 0; i<this.editor.btns.length; i++) {
23897                 children.push(this.editor.btns[i]);
23898             }
23899         }
23900         
23901         /*
23902         var cog = {
23903                 xtype: 'Button',
23904                 size : 'sm',
23905                 xns: Roo.bootstrap,
23906                 glyphicon : 'cog',
23907                 //html : 'submit'
23908                 menu : {
23909                     xtype: 'Menu',
23910                     xns: Roo.bootstrap,
23911                     items:  []
23912                 }
23913         };
23914         
23915         cog.menu.items.push({
23916             xtype :'MenuItem',
23917             xns: Roo.bootstrap,
23918             html : Clean styles,
23919             tagname : f,
23920             listeners : {
23921                 click : function()
23922                 {
23923                     editorcore.insertTag(this.tagname);
23924                     editor.focus();
23925                 }
23926             }
23927             
23928         });
23929        */
23930         
23931          
23932        this.xtype = 'NavSimplebar';
23933         
23934         for(var i=0;i< children.length;i++) {
23935             
23936             this.buttons.add(this.addxtypeChild(children[i]));
23937             
23938         }
23939         
23940         editor.on('editorevent', this.updateToolbar, this);
23941     },
23942     onBtnClick : function(id)
23943     {
23944        this.editorcore.relayCmd(id);
23945        this.editorcore.focus();
23946     },
23947     
23948     /**
23949      * Protected method that will not generally be called directly. It triggers
23950      * a toolbar update by reading the markup state of the current selection in the editor.
23951      */
23952     updateToolbar: function(){
23953
23954         if(!this.editorcore.activated){
23955             this.editor.onFirstFocus(); // is this neeed?
23956             return;
23957         }
23958
23959         var btns = this.buttons; 
23960         var doc = this.editorcore.doc;
23961         btns.get('bold').setActive(doc.queryCommandState('bold'));
23962         btns.get('italic').setActive(doc.queryCommandState('italic'));
23963         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23964         
23965         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23966         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23967         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23968         
23969         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23970         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23971          /*
23972         
23973         var ans = this.editorcore.getAllAncestors();
23974         if (this.formatCombo) {
23975             
23976             
23977             var store = this.formatCombo.store;
23978             this.formatCombo.setValue("");
23979             for (var i =0; i < ans.length;i++) {
23980                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23981                     // select it..
23982                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23983                     break;
23984                 }
23985             }
23986         }
23987         
23988         
23989         
23990         // hides menus... - so this cant be on a menu...
23991         Roo.bootstrap.MenuMgr.hideAll();
23992         */
23993         Roo.bootstrap.MenuMgr.hideAll();
23994         //this.editorsyncValue();
23995     },
23996     onFirstFocus: function() {
23997         this.buttons.each(function(item){
23998            item.enable();
23999         });
24000     },
24001     toggleSourceEdit : function(sourceEditMode){
24002         
24003           
24004         if(sourceEditMode){
24005             Roo.log("disabling buttons");
24006            this.buttons.each( function(item){
24007                 if(item.cmd != 'pencil'){
24008                     item.disable();
24009                 }
24010             });
24011           
24012         }else{
24013             Roo.log("enabling buttons");
24014             if(this.editorcore.initialized){
24015                 this.buttons.each( function(item){
24016                     item.enable();
24017                 });
24018             }
24019             
24020         }
24021         Roo.log("calling toggole on editor");
24022         // tell the editor that it's been pressed..
24023         this.editor.toggleSourceEdit(sourceEditMode);
24024        
24025     }
24026 });
24027
24028
24029
24030
24031
24032 /**
24033  * @class Roo.bootstrap.Table.AbstractSelectionModel
24034  * @extends Roo.util.Observable
24035  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24036  * implemented by descendant classes.  This class should not be directly instantiated.
24037  * @constructor
24038  */
24039 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24040     this.locked = false;
24041     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24042 };
24043
24044
24045 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24046     /** @ignore Called by the grid automatically. Do not call directly. */
24047     init : function(grid){
24048         this.grid = grid;
24049         this.initEvents();
24050     },
24051
24052     /**
24053      * Locks the selections.
24054      */
24055     lock : function(){
24056         this.locked = true;
24057     },
24058
24059     /**
24060      * Unlocks the selections.
24061      */
24062     unlock : function(){
24063         this.locked = false;
24064     },
24065
24066     /**
24067      * Returns true if the selections are locked.
24068      * @return {Boolean}
24069      */
24070     isLocked : function(){
24071         return this.locked;
24072     }
24073 });
24074 /**
24075  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24076  * @class Roo.bootstrap.Table.RowSelectionModel
24077  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24078  * It supports multiple selections and keyboard selection/navigation. 
24079  * @constructor
24080  * @param {Object} config
24081  */
24082
24083 Roo.bootstrap.Table.RowSelectionModel = function(config){
24084     Roo.apply(this, config);
24085     this.selections = new Roo.util.MixedCollection(false, function(o){
24086         return o.id;
24087     });
24088
24089     this.last = false;
24090     this.lastActive = false;
24091
24092     this.addEvents({
24093         /**
24094              * @event selectionchange
24095              * Fires when the selection changes
24096              * @param {SelectionModel} this
24097              */
24098             "selectionchange" : true,
24099         /**
24100              * @event afterselectionchange
24101              * Fires after the selection changes (eg. by key press or clicking)
24102              * @param {SelectionModel} this
24103              */
24104             "afterselectionchange" : true,
24105         /**
24106              * @event beforerowselect
24107              * Fires when a row is selected being selected, return false to cancel.
24108              * @param {SelectionModel} this
24109              * @param {Number} rowIndex The selected index
24110              * @param {Boolean} keepExisting False if other selections will be cleared
24111              */
24112             "beforerowselect" : true,
24113         /**
24114              * @event rowselect
24115              * Fires when a row is selected.
24116              * @param {SelectionModel} this
24117              * @param {Number} rowIndex The selected index
24118              * @param {Roo.data.Record} r The record
24119              */
24120             "rowselect" : true,
24121         /**
24122              * @event rowdeselect
24123              * Fires when a row is deselected.
24124              * @param {SelectionModel} this
24125              * @param {Number} rowIndex The selected index
24126              */
24127         "rowdeselect" : true
24128     });
24129     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24130     this.locked = false;
24131  };
24132
24133 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24134     /**
24135      * @cfg {Boolean} singleSelect
24136      * True to allow selection of only one row at a time (defaults to false)
24137      */
24138     singleSelect : false,
24139
24140     // private
24141     initEvents : function()
24142     {
24143
24144         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24145         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24146         //}else{ // allow click to work like normal
24147          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24148         //}
24149         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24150         this.grid.on("rowclick", this.handleMouseDown, this);
24151         
24152         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24153             "up" : function(e){
24154                 if(!e.shiftKey){
24155                     this.selectPrevious(e.shiftKey);
24156                 }else if(this.last !== false && this.lastActive !== false){
24157                     var last = this.last;
24158                     this.selectRange(this.last,  this.lastActive-1);
24159                     this.grid.getView().focusRow(this.lastActive);
24160                     if(last !== false){
24161                         this.last = last;
24162                     }
24163                 }else{
24164                     this.selectFirstRow();
24165                 }
24166                 this.fireEvent("afterselectionchange", this);
24167             },
24168             "down" : function(e){
24169                 if(!e.shiftKey){
24170                     this.selectNext(e.shiftKey);
24171                 }else if(this.last !== false && this.lastActive !== false){
24172                     var last = this.last;
24173                     this.selectRange(this.last,  this.lastActive+1);
24174                     this.grid.getView().focusRow(this.lastActive);
24175                     if(last !== false){
24176                         this.last = last;
24177                     }
24178                 }else{
24179                     this.selectFirstRow();
24180                 }
24181                 this.fireEvent("afterselectionchange", this);
24182             },
24183             scope: this
24184         });
24185         this.grid.store.on('load', function(){
24186             this.selections.clear();
24187         },this);
24188         /*
24189         var view = this.grid.view;
24190         view.on("refresh", this.onRefresh, this);
24191         view.on("rowupdated", this.onRowUpdated, this);
24192         view.on("rowremoved", this.onRemove, this);
24193         */
24194     },
24195
24196     // private
24197     onRefresh : function()
24198     {
24199         var ds = this.grid.store, i, v = this.grid.view;
24200         var s = this.selections;
24201         s.each(function(r){
24202             if((i = ds.indexOfId(r.id)) != -1){
24203                 v.onRowSelect(i);
24204             }else{
24205                 s.remove(r);
24206             }
24207         });
24208     },
24209
24210     // private
24211     onRemove : function(v, index, r){
24212         this.selections.remove(r);
24213     },
24214
24215     // private
24216     onRowUpdated : function(v, index, r){
24217         if(this.isSelected(r)){
24218             v.onRowSelect(index);
24219         }
24220     },
24221
24222     /**
24223      * Select records.
24224      * @param {Array} records The records to select
24225      * @param {Boolean} keepExisting (optional) True to keep existing selections
24226      */
24227     selectRecords : function(records, keepExisting)
24228     {
24229         if(!keepExisting){
24230             this.clearSelections();
24231         }
24232             var ds = this.grid.store;
24233         for(var i = 0, len = records.length; i < len; i++){
24234             this.selectRow(ds.indexOf(records[i]), true);
24235         }
24236     },
24237
24238     /**
24239      * Gets the number of selected rows.
24240      * @return {Number}
24241      */
24242     getCount : function(){
24243         return this.selections.length;
24244     },
24245
24246     /**
24247      * Selects the first row in the grid.
24248      */
24249     selectFirstRow : function(){
24250         this.selectRow(0);
24251     },
24252
24253     /**
24254      * Select the last row.
24255      * @param {Boolean} keepExisting (optional) True to keep existing selections
24256      */
24257     selectLastRow : function(keepExisting){
24258         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24259         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24260     },
24261
24262     /**
24263      * Selects the row immediately following the last selected row.
24264      * @param {Boolean} keepExisting (optional) True to keep existing selections
24265      */
24266     selectNext : function(keepExisting)
24267     {
24268             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24269             this.selectRow(this.last+1, keepExisting);
24270             this.grid.getView().focusRow(this.last);
24271         }
24272     },
24273
24274     /**
24275      * Selects the row that precedes the last selected row.
24276      * @param {Boolean} keepExisting (optional) True to keep existing selections
24277      */
24278     selectPrevious : function(keepExisting){
24279         if(this.last){
24280             this.selectRow(this.last-1, keepExisting);
24281             this.grid.getView().focusRow(this.last);
24282         }
24283     },
24284
24285     /**
24286      * Returns the selected records
24287      * @return {Array} Array of selected records
24288      */
24289     getSelections : function(){
24290         return [].concat(this.selections.items);
24291     },
24292
24293     /**
24294      * Returns the first selected record.
24295      * @return {Record}
24296      */
24297     getSelected : function(){
24298         return this.selections.itemAt(0);
24299     },
24300
24301
24302     /**
24303      * Clears all selections.
24304      */
24305     clearSelections : function(fast)
24306     {
24307         if(this.locked) {
24308             return;
24309         }
24310         if(fast !== true){
24311                 var ds = this.grid.store;
24312             var s = this.selections;
24313             s.each(function(r){
24314                 this.deselectRow(ds.indexOfId(r.id));
24315             }, this);
24316             s.clear();
24317         }else{
24318             this.selections.clear();
24319         }
24320         this.last = false;
24321     },
24322
24323
24324     /**
24325      * Selects all rows.
24326      */
24327     selectAll : function(){
24328         if(this.locked) {
24329             return;
24330         }
24331         this.selections.clear();
24332         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24333             this.selectRow(i, true);
24334         }
24335     },
24336
24337     /**
24338      * Returns True if there is a selection.
24339      * @return {Boolean}
24340      */
24341     hasSelection : function(){
24342         return this.selections.length > 0;
24343     },
24344
24345     /**
24346      * Returns True if the specified row is selected.
24347      * @param {Number/Record} record The record or index of the record to check
24348      * @return {Boolean}
24349      */
24350     isSelected : function(index){
24351             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24352         return (r && this.selections.key(r.id) ? true : false);
24353     },
24354
24355     /**
24356      * Returns True if the specified record id is selected.
24357      * @param {String} id The id of record to check
24358      * @return {Boolean}
24359      */
24360     isIdSelected : function(id){
24361         return (this.selections.key(id) ? true : false);
24362     },
24363
24364
24365     // private
24366     handleMouseDBClick : function(e, t){
24367         
24368     },
24369     // private
24370     handleMouseDown : function(e, t)
24371     {
24372             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24373         if(this.isLocked() || rowIndex < 0 ){
24374             return;
24375         };
24376         if(e.shiftKey && this.last !== false){
24377             var last = this.last;
24378             this.selectRange(last, rowIndex, e.ctrlKey);
24379             this.last = last; // reset the last
24380             t.focus();
24381     
24382         }else{
24383             var isSelected = this.isSelected(rowIndex);
24384             //Roo.log("select row:" + rowIndex);
24385             if(isSelected){
24386                 this.deselectRow(rowIndex);
24387             } else {
24388                         this.selectRow(rowIndex, true);
24389             }
24390     
24391             /*
24392                 if(e.button !== 0 && isSelected){
24393                 alert('rowIndex 2: ' + rowIndex);
24394                     view.focusRow(rowIndex);
24395                 }else if(e.ctrlKey && isSelected){
24396                     this.deselectRow(rowIndex);
24397                 }else if(!isSelected){
24398                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24399                     view.focusRow(rowIndex);
24400                 }
24401             */
24402         }
24403         this.fireEvent("afterselectionchange", this);
24404     },
24405     // private
24406     handleDragableRowClick :  function(grid, rowIndex, e) 
24407     {
24408         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24409             this.selectRow(rowIndex, false);
24410             grid.view.focusRow(rowIndex);
24411              this.fireEvent("afterselectionchange", this);
24412         }
24413     },
24414     
24415     /**
24416      * Selects multiple rows.
24417      * @param {Array} rows Array of the indexes of the row to select
24418      * @param {Boolean} keepExisting (optional) True to keep existing selections
24419      */
24420     selectRows : function(rows, keepExisting){
24421         if(!keepExisting){
24422             this.clearSelections();
24423         }
24424         for(var i = 0, len = rows.length; i < len; i++){
24425             this.selectRow(rows[i], true);
24426         }
24427     },
24428
24429     /**
24430      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24431      * @param {Number} startRow The index of the first row in the range
24432      * @param {Number} endRow The index of the last row in the range
24433      * @param {Boolean} keepExisting (optional) True to retain existing selections
24434      */
24435     selectRange : function(startRow, endRow, keepExisting){
24436         if(this.locked) {
24437             return;
24438         }
24439         if(!keepExisting){
24440             this.clearSelections();
24441         }
24442         if(startRow <= endRow){
24443             for(var i = startRow; i <= endRow; i++){
24444                 this.selectRow(i, true);
24445             }
24446         }else{
24447             for(var i = startRow; i >= endRow; i--){
24448                 this.selectRow(i, true);
24449             }
24450         }
24451     },
24452
24453     /**
24454      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24455      * @param {Number} startRow The index of the first row in the range
24456      * @param {Number} endRow The index of the last row in the range
24457      */
24458     deselectRange : function(startRow, endRow, preventViewNotify){
24459         if(this.locked) {
24460             return;
24461         }
24462         for(var i = startRow; i <= endRow; i++){
24463             this.deselectRow(i, preventViewNotify);
24464         }
24465     },
24466
24467     /**
24468      * Selects a row.
24469      * @param {Number} row The index of the row to select
24470      * @param {Boolean} keepExisting (optional) True to keep existing selections
24471      */
24472     selectRow : function(index, keepExisting, preventViewNotify)
24473     {
24474             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24475             return;
24476         }
24477         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24478             if(!keepExisting || this.singleSelect){
24479                 this.clearSelections();
24480             }
24481             
24482             var r = this.grid.store.getAt(index);
24483             //console.log('selectRow - record id :' + r.id);
24484             
24485             this.selections.add(r);
24486             this.last = this.lastActive = index;
24487             if(!preventViewNotify){
24488                 var proxy = new Roo.Element(
24489                                 this.grid.getRowDom(index)
24490                 );
24491                 proxy.addClass('bg-info info');
24492             }
24493             this.fireEvent("rowselect", this, index, r);
24494             this.fireEvent("selectionchange", this);
24495         }
24496     },
24497
24498     /**
24499      * Deselects a row.
24500      * @param {Number} row The index of the row to deselect
24501      */
24502     deselectRow : function(index, preventViewNotify)
24503     {
24504         if(this.locked) {
24505             return;
24506         }
24507         if(this.last == index){
24508             this.last = false;
24509         }
24510         if(this.lastActive == index){
24511             this.lastActive = false;
24512         }
24513         
24514         var r = this.grid.store.getAt(index);
24515         if (!r) {
24516             return;
24517         }
24518         
24519         this.selections.remove(r);
24520         //.console.log('deselectRow - record id :' + r.id);
24521         if(!preventViewNotify){
24522         
24523             var proxy = new Roo.Element(
24524                 this.grid.getRowDom(index)
24525             );
24526             proxy.removeClass('bg-info info');
24527         }
24528         this.fireEvent("rowdeselect", this, index);
24529         this.fireEvent("selectionchange", this);
24530     },
24531
24532     // private
24533     restoreLast : function(){
24534         if(this._last){
24535             this.last = this._last;
24536         }
24537     },
24538
24539     // private
24540     acceptsNav : function(row, col, cm){
24541         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24542     },
24543
24544     // private
24545     onEditorKey : function(field, e){
24546         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24547         if(k == e.TAB){
24548             e.stopEvent();
24549             ed.completeEdit();
24550             if(e.shiftKey){
24551                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24552             }else{
24553                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24554             }
24555         }else if(k == e.ENTER && !e.ctrlKey){
24556             e.stopEvent();
24557             ed.completeEdit();
24558             if(e.shiftKey){
24559                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24560             }else{
24561                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24562             }
24563         }else if(k == e.ESC){
24564             ed.cancelEdit();
24565         }
24566         if(newCell){
24567             g.startEditing(newCell[0], newCell[1]);
24568         }
24569     }
24570 });
24571 /*
24572  * Based on:
24573  * Ext JS Library 1.1.1
24574  * Copyright(c) 2006-2007, Ext JS, LLC.
24575  *
24576  * Originally Released Under LGPL - original licence link has changed is not relivant.
24577  *
24578  * Fork - LGPL
24579  * <script type="text/javascript">
24580  */
24581  
24582 /**
24583  * @class Roo.bootstrap.PagingToolbar
24584  * @extends Roo.bootstrap.NavSimplebar
24585  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24586  * @constructor
24587  * Create a new PagingToolbar
24588  * @param {Object} config The config object
24589  * @param {Roo.data.Store} store
24590  */
24591 Roo.bootstrap.PagingToolbar = function(config)
24592 {
24593     // old args format still supported... - xtype is prefered..
24594         // created from xtype...
24595     
24596     this.ds = config.dataSource;
24597     
24598     if (config.store && !this.ds) {
24599         this.store= Roo.factory(config.store, Roo.data);
24600         this.ds = this.store;
24601         this.ds.xmodule = this.xmodule || false;
24602     }
24603     
24604     this.toolbarItems = [];
24605     if (config.items) {
24606         this.toolbarItems = config.items;
24607     }
24608     
24609     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24610     
24611     this.cursor = 0;
24612     
24613     if (this.ds) { 
24614         this.bind(this.ds);
24615     }
24616     
24617     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24618     
24619 };
24620
24621 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24622     /**
24623      * @cfg {Roo.data.Store} dataSource
24624      * The underlying data store providing the paged data
24625      */
24626     /**
24627      * @cfg {String/HTMLElement/Element} container
24628      * container The id or element that will contain the toolbar
24629      */
24630     /**
24631      * @cfg {Boolean} displayInfo
24632      * True to display the displayMsg (defaults to false)
24633      */
24634     /**
24635      * @cfg {Number} pageSize
24636      * The number of records to display per page (defaults to 20)
24637      */
24638     pageSize: 20,
24639     /**
24640      * @cfg {String} displayMsg
24641      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24642      */
24643     displayMsg : 'Displaying {0} - {1} of {2}',
24644     /**
24645      * @cfg {String} emptyMsg
24646      * The message to display when no records are found (defaults to "No data to display")
24647      */
24648     emptyMsg : 'No data to display',
24649     /**
24650      * Customizable piece of the default paging text (defaults to "Page")
24651      * @type String
24652      */
24653     beforePageText : "Page",
24654     /**
24655      * Customizable piece of the default paging text (defaults to "of %0")
24656      * @type String
24657      */
24658     afterPageText : "of {0}",
24659     /**
24660      * Customizable piece of the default paging text (defaults to "First Page")
24661      * @type String
24662      */
24663     firstText : "First Page",
24664     /**
24665      * Customizable piece of the default paging text (defaults to "Previous Page")
24666      * @type String
24667      */
24668     prevText : "Previous Page",
24669     /**
24670      * Customizable piece of the default paging text (defaults to "Next Page")
24671      * @type String
24672      */
24673     nextText : "Next Page",
24674     /**
24675      * Customizable piece of the default paging text (defaults to "Last Page")
24676      * @type String
24677      */
24678     lastText : "Last Page",
24679     /**
24680      * Customizable piece of the default paging text (defaults to "Refresh")
24681      * @type String
24682      */
24683     refreshText : "Refresh",
24684
24685     buttons : false,
24686     // private
24687     onRender : function(ct, position) 
24688     {
24689         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24690         this.navgroup.parentId = this.id;
24691         this.navgroup.onRender(this.el, null);
24692         // add the buttons to the navgroup
24693         
24694         if(this.displayInfo){
24695             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24696             this.displayEl = this.el.select('.x-paging-info', true).first();
24697 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24698 //            this.displayEl = navel.el.select('span',true).first();
24699         }
24700         
24701         var _this = this;
24702         
24703         if(this.buttons){
24704             Roo.each(_this.buttons, function(e){ // this might need to use render????
24705                Roo.factory(e).render(_this.el);
24706             });
24707         }
24708             
24709         Roo.each(_this.toolbarItems, function(e) {
24710             _this.navgroup.addItem(e);
24711         });
24712         
24713         
24714         this.first = this.navgroup.addItem({
24715             tooltip: this.firstText,
24716             cls: "prev",
24717             icon : 'fa fa-backward',
24718             disabled: true,
24719             preventDefault: true,
24720             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24721         });
24722         
24723         this.prev =  this.navgroup.addItem({
24724             tooltip: this.prevText,
24725             cls: "prev",
24726             icon : 'fa fa-step-backward',
24727             disabled: true,
24728             preventDefault: true,
24729             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24730         });
24731     //this.addSeparator();
24732         
24733         
24734         var field = this.navgroup.addItem( {
24735             tagtype : 'span',
24736             cls : 'x-paging-position',
24737             
24738             html : this.beforePageText  +
24739                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24740                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24741          } ); //?? escaped?
24742         
24743         this.field = field.el.select('input', true).first();
24744         this.field.on("keydown", this.onPagingKeydown, this);
24745         this.field.on("focus", function(){this.dom.select();});
24746     
24747     
24748         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24749         //this.field.setHeight(18);
24750         //this.addSeparator();
24751         this.next = this.navgroup.addItem({
24752             tooltip: this.nextText,
24753             cls: "next",
24754             html : ' <i class="fa fa-step-forward">',
24755             disabled: true,
24756             preventDefault: true,
24757             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24758         });
24759         this.last = this.navgroup.addItem({
24760             tooltip: this.lastText,
24761             icon : 'fa fa-forward',
24762             cls: "next",
24763             disabled: true,
24764             preventDefault: true,
24765             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24766         });
24767     //this.addSeparator();
24768         this.loading = this.navgroup.addItem({
24769             tooltip: this.refreshText,
24770             icon: 'fa fa-refresh',
24771             preventDefault: true,
24772             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24773         });
24774         
24775     },
24776
24777     // private
24778     updateInfo : function(){
24779         if(this.displayEl){
24780             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24781             var msg = count == 0 ?
24782                 this.emptyMsg :
24783                 String.format(
24784                     this.displayMsg,
24785                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24786                 );
24787             this.displayEl.update(msg);
24788         }
24789     },
24790
24791     // private
24792     onLoad : function(ds, r, o)
24793     {
24794         this.cursor = o.params.start ? o.params.start : 0;
24795         
24796         var d = this.getPageData(),
24797             ap = d.activePage,
24798             ps = d.pages;
24799         
24800         
24801         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24802         this.field.dom.value = ap;
24803         this.first.setDisabled(ap == 1);
24804         this.prev.setDisabled(ap == 1);
24805         this.next.setDisabled(ap == ps);
24806         this.last.setDisabled(ap == ps);
24807         this.loading.enable();
24808         this.updateInfo();
24809     },
24810
24811     // private
24812     getPageData : function(){
24813         var total = this.ds.getTotalCount();
24814         return {
24815             total : total,
24816             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24817             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24818         };
24819     },
24820
24821     // private
24822     onLoadError : function(){
24823         this.loading.enable();
24824     },
24825
24826     // private
24827     onPagingKeydown : function(e){
24828         var k = e.getKey();
24829         var d = this.getPageData();
24830         if(k == e.RETURN){
24831             var v = this.field.dom.value, pageNum;
24832             if(!v || isNaN(pageNum = parseInt(v, 10))){
24833                 this.field.dom.value = d.activePage;
24834                 return;
24835             }
24836             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24837             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24838             e.stopEvent();
24839         }
24840         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))
24841         {
24842           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24843           this.field.dom.value = pageNum;
24844           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24845           e.stopEvent();
24846         }
24847         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24848         {
24849           var v = this.field.dom.value, pageNum; 
24850           var increment = (e.shiftKey) ? 10 : 1;
24851           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24852                 increment *= -1;
24853           }
24854           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24855             this.field.dom.value = d.activePage;
24856             return;
24857           }
24858           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24859           {
24860             this.field.dom.value = parseInt(v, 10) + increment;
24861             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24862             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24863           }
24864           e.stopEvent();
24865         }
24866     },
24867
24868     // private
24869     beforeLoad : function(){
24870         if(this.loading){
24871             this.loading.disable();
24872         }
24873     },
24874
24875     // private
24876     onClick : function(which){
24877         
24878         var ds = this.ds;
24879         if (!ds) {
24880             return;
24881         }
24882         
24883         switch(which){
24884             case "first":
24885                 ds.load({params:{start: 0, limit: this.pageSize}});
24886             break;
24887             case "prev":
24888                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24889             break;
24890             case "next":
24891                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24892             break;
24893             case "last":
24894                 var total = ds.getTotalCount();
24895                 var extra = total % this.pageSize;
24896                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24897                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24898             break;
24899             case "refresh":
24900                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24901             break;
24902         }
24903     },
24904
24905     /**
24906      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24907      * @param {Roo.data.Store} store The data store to unbind
24908      */
24909     unbind : function(ds){
24910         ds.un("beforeload", this.beforeLoad, this);
24911         ds.un("load", this.onLoad, this);
24912         ds.un("loadexception", this.onLoadError, this);
24913         ds.un("remove", this.updateInfo, this);
24914         ds.un("add", this.updateInfo, this);
24915         this.ds = undefined;
24916     },
24917
24918     /**
24919      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24920      * @param {Roo.data.Store} store The data store to bind
24921      */
24922     bind : function(ds){
24923         ds.on("beforeload", this.beforeLoad, this);
24924         ds.on("load", this.onLoad, this);
24925         ds.on("loadexception", this.onLoadError, this);
24926         ds.on("remove", this.updateInfo, this);
24927         ds.on("add", this.updateInfo, this);
24928         this.ds = ds;
24929     }
24930 });/*
24931  * - LGPL
24932  *
24933  * element
24934  * 
24935  */
24936
24937 /**
24938  * @class Roo.bootstrap.MessageBar
24939  * @extends Roo.bootstrap.Component
24940  * Bootstrap MessageBar class
24941  * @cfg {String} html contents of the MessageBar
24942  * @cfg {String} weight (info | success | warning | danger) default info
24943  * @cfg {String} beforeClass insert the bar before the given class
24944  * @cfg {Boolean} closable (true | false) default false
24945  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24946  * 
24947  * @constructor
24948  * Create a new Element
24949  * @param {Object} config The config object
24950  */
24951
24952 Roo.bootstrap.MessageBar = function(config){
24953     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24954 };
24955
24956 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24957     
24958     html: '',
24959     weight: 'info',
24960     closable: false,
24961     fixed: false,
24962     beforeClass: 'bootstrap-sticky-wrap',
24963     
24964     getAutoCreate : function(){
24965         
24966         var cfg = {
24967             tag: 'div',
24968             cls: 'alert alert-dismissable alert-' + this.weight,
24969             cn: [
24970                 {
24971                     tag: 'span',
24972                     cls: 'message',
24973                     html: this.html || ''
24974                 }
24975             ]
24976         };
24977         
24978         if(this.fixed){
24979             cfg.cls += ' alert-messages-fixed';
24980         }
24981         
24982         if(this.closable){
24983             cfg.cn.push({
24984                 tag: 'button',
24985                 cls: 'close',
24986                 html: 'x'
24987             });
24988         }
24989         
24990         return cfg;
24991     },
24992     
24993     onRender : function(ct, position)
24994     {
24995         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24996         
24997         if(!this.el){
24998             var cfg = Roo.apply({},  this.getAutoCreate());
24999             cfg.id = Roo.id();
25000             
25001             if (this.cls) {
25002                 cfg.cls += ' ' + this.cls;
25003             }
25004             if (this.style) {
25005                 cfg.style = this.style;
25006             }
25007             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25008             
25009             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25010         }
25011         
25012         this.el.select('>button.close').on('click', this.hide, this);
25013         
25014     },
25015     
25016     show : function()
25017     {
25018         if (!this.rendered) {
25019             this.render();
25020         }
25021         
25022         this.el.show();
25023         
25024         this.fireEvent('show', this);
25025         
25026     },
25027     
25028     hide : function()
25029     {
25030         if (!this.rendered) {
25031             this.render();
25032         }
25033         
25034         this.el.hide();
25035         
25036         this.fireEvent('hide', this);
25037     },
25038     
25039     update : function()
25040     {
25041 //        var e = this.el.dom.firstChild;
25042 //        
25043 //        if(this.closable){
25044 //            e = e.nextSibling;
25045 //        }
25046 //        
25047 //        e.data = this.html || '';
25048
25049         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25050     }
25051    
25052 });
25053
25054  
25055
25056      /*
25057  * - LGPL
25058  *
25059  * Graph
25060  * 
25061  */
25062
25063
25064 /**
25065  * @class Roo.bootstrap.Graph
25066  * @extends Roo.bootstrap.Component
25067  * Bootstrap Graph class
25068 > Prameters
25069  -sm {number} sm 4
25070  -md {number} md 5
25071  @cfg {String} graphtype  bar | vbar | pie
25072  @cfg {number} g_x coodinator | centre x (pie)
25073  @cfg {number} g_y coodinator | centre y (pie)
25074  @cfg {number} g_r radius (pie)
25075  @cfg {number} g_height height of the chart (respected by all elements in the set)
25076  @cfg {number} g_width width of the chart (respected by all elements in the set)
25077  @cfg {Object} title The title of the chart
25078     
25079  -{Array}  values
25080  -opts (object) options for the chart 
25081      o {
25082      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25083      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25084      o vgutter (number)
25085      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.
25086      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25087      o to
25088      o stretch (boolean)
25089      o }
25090  -opts (object) options for the pie
25091      o{
25092      o cut
25093      o startAngle (number)
25094      o endAngle (number)
25095      } 
25096  *
25097  * @constructor
25098  * Create a new Input
25099  * @param {Object} config The config object
25100  */
25101
25102 Roo.bootstrap.Graph = function(config){
25103     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25104     
25105     this.addEvents({
25106         // img events
25107         /**
25108          * @event click
25109          * The img click event for the img.
25110          * @param {Roo.EventObject} e
25111          */
25112         "click" : true
25113     });
25114 };
25115
25116 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25117     
25118     sm: 4,
25119     md: 5,
25120     graphtype: 'bar',
25121     g_height: 250,
25122     g_width: 400,
25123     g_x: 50,
25124     g_y: 50,
25125     g_r: 30,
25126     opts:{
25127         //g_colors: this.colors,
25128         g_type: 'soft',
25129         g_gutter: '20%'
25130
25131     },
25132     title : false,
25133
25134     getAutoCreate : function(){
25135         
25136         var cfg = {
25137             tag: 'div',
25138             html : null
25139         };
25140         
25141         
25142         return  cfg;
25143     },
25144
25145     onRender : function(ct,position){
25146         
25147         
25148         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25149         
25150         if (typeof(Raphael) == 'undefined') {
25151             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25152             return;
25153         }
25154         
25155         this.raphael = Raphael(this.el.dom);
25156         
25157                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25158                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25159                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25160                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25161                 /*
25162                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25163                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25164                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25165                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25166                 
25167                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25168                 r.barchart(330, 10, 300, 220, data1);
25169                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25170                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25171                 */
25172                 
25173                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25174                 // r.barchart(30, 30, 560, 250,  xdata, {
25175                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25176                 //     axis : "0 0 1 1",
25177                 //     axisxlabels :  xdata
25178                 //     //yvalues : cols,
25179                    
25180                 // });
25181 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25182 //        
25183 //        this.load(null,xdata,{
25184 //                axis : "0 0 1 1",
25185 //                axisxlabels :  xdata
25186 //                });
25187
25188     },
25189
25190     load : function(graphtype,xdata,opts)
25191     {
25192         this.raphael.clear();
25193         if(!graphtype) {
25194             graphtype = this.graphtype;
25195         }
25196         if(!opts){
25197             opts = this.opts;
25198         }
25199         var r = this.raphael,
25200             fin = function () {
25201                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25202             },
25203             fout = function () {
25204                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25205             },
25206             pfin = function() {
25207                 this.sector.stop();
25208                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25209
25210                 if (this.label) {
25211                     this.label[0].stop();
25212                     this.label[0].attr({ r: 7.5 });
25213                     this.label[1].attr({ "font-weight": 800 });
25214                 }
25215             },
25216             pfout = function() {
25217                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25218
25219                 if (this.label) {
25220                     this.label[0].animate({ r: 5 }, 500, "bounce");
25221                     this.label[1].attr({ "font-weight": 400 });
25222                 }
25223             };
25224
25225         switch(graphtype){
25226             case 'bar':
25227                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25228                 break;
25229             case 'hbar':
25230                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25231                 break;
25232             case 'pie':
25233 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25234 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25235 //            
25236                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25237                 
25238                 break;
25239
25240         }
25241         
25242         if(this.title){
25243             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25244         }
25245         
25246     },
25247     
25248     setTitle: function(o)
25249     {
25250         this.title = o;
25251     },
25252     
25253     initEvents: function() {
25254         
25255         if(!this.href){
25256             this.el.on('click', this.onClick, this);
25257         }
25258     },
25259     
25260     onClick : function(e)
25261     {
25262         Roo.log('img onclick');
25263         this.fireEvent('click', this, e);
25264     }
25265    
25266 });
25267
25268  
25269 /*
25270  * - LGPL
25271  *
25272  * numberBox
25273  * 
25274  */
25275 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25276
25277 /**
25278  * @class Roo.bootstrap.dash.NumberBox
25279  * @extends Roo.bootstrap.Component
25280  * Bootstrap NumberBox class
25281  * @cfg {String} headline Box headline
25282  * @cfg {String} content Box content
25283  * @cfg {String} icon Box icon
25284  * @cfg {String} footer Footer text
25285  * @cfg {String} fhref Footer href
25286  * 
25287  * @constructor
25288  * Create a new NumberBox
25289  * @param {Object} config The config object
25290  */
25291
25292
25293 Roo.bootstrap.dash.NumberBox = function(config){
25294     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25295     
25296 };
25297
25298 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25299     
25300     headline : '',
25301     content : '',
25302     icon : '',
25303     footer : '',
25304     fhref : '',
25305     ficon : '',
25306     
25307     getAutoCreate : function(){
25308         
25309         var cfg = {
25310             tag : 'div',
25311             cls : 'small-box ',
25312             cn : [
25313                 {
25314                     tag : 'div',
25315                     cls : 'inner',
25316                     cn :[
25317                         {
25318                             tag : 'h3',
25319                             cls : 'roo-headline',
25320                             html : this.headline
25321                         },
25322                         {
25323                             tag : 'p',
25324                             cls : 'roo-content',
25325                             html : this.content
25326                         }
25327                     ]
25328                 }
25329             ]
25330         };
25331         
25332         if(this.icon){
25333             cfg.cn.push({
25334                 tag : 'div',
25335                 cls : 'icon',
25336                 cn :[
25337                     {
25338                         tag : 'i',
25339                         cls : 'ion ' + this.icon
25340                     }
25341                 ]
25342             });
25343         }
25344         
25345         if(this.footer){
25346             var footer = {
25347                 tag : 'a',
25348                 cls : 'small-box-footer',
25349                 href : this.fhref || '#',
25350                 html : this.footer
25351             };
25352             
25353             cfg.cn.push(footer);
25354             
25355         }
25356         
25357         return  cfg;
25358     },
25359
25360     onRender : function(ct,position){
25361         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25362
25363
25364        
25365                 
25366     },
25367
25368     setHeadline: function (value)
25369     {
25370         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25371     },
25372     
25373     setFooter: function (value, href)
25374     {
25375         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25376         
25377         if(href){
25378             this.el.select('a.small-box-footer',true).first().attr('href', href);
25379         }
25380         
25381     },
25382
25383     setContent: function (value)
25384     {
25385         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25386     },
25387
25388     initEvents: function() 
25389     {   
25390         
25391     }
25392     
25393 });
25394
25395  
25396 /*
25397  * - LGPL
25398  *
25399  * TabBox
25400  * 
25401  */
25402 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25403
25404 /**
25405  * @class Roo.bootstrap.dash.TabBox
25406  * @extends Roo.bootstrap.Component
25407  * Bootstrap TabBox class
25408  * @cfg {String} title Title of the TabBox
25409  * @cfg {String} icon Icon of the TabBox
25410  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25411  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25412  * 
25413  * @constructor
25414  * Create a new TabBox
25415  * @param {Object} config The config object
25416  */
25417
25418
25419 Roo.bootstrap.dash.TabBox = function(config){
25420     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25421     this.addEvents({
25422         // raw events
25423         /**
25424          * @event addpane
25425          * When a pane is added
25426          * @param {Roo.bootstrap.dash.TabPane} pane
25427          */
25428         "addpane" : true,
25429         /**
25430          * @event activatepane
25431          * When a pane is activated
25432          * @param {Roo.bootstrap.dash.TabPane} pane
25433          */
25434         "activatepane" : true
25435         
25436          
25437     });
25438     
25439     this.panes = [];
25440 };
25441
25442 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25443
25444     title : '',
25445     icon : false,
25446     showtabs : true,
25447     tabScrollable : false,
25448     
25449     getChildContainer : function()
25450     {
25451         return this.el.select('.tab-content', true).first();
25452     },
25453     
25454     getAutoCreate : function(){
25455         
25456         var header = {
25457             tag: 'li',
25458             cls: 'pull-left header',
25459             html: this.title,
25460             cn : []
25461         };
25462         
25463         if(this.icon){
25464             header.cn.push({
25465                 tag: 'i',
25466                 cls: 'fa ' + this.icon
25467             });
25468         }
25469         
25470         var h = {
25471             tag: 'ul',
25472             cls: 'nav nav-tabs pull-right',
25473             cn: [
25474                 header
25475             ]
25476         };
25477         
25478         if(this.tabScrollable){
25479             h = {
25480                 tag: 'div',
25481                 cls: 'tab-header',
25482                 cn: [
25483                     {
25484                         tag: 'ul',
25485                         cls: 'nav nav-tabs pull-right',
25486                         cn: [
25487                             header
25488                         ]
25489                     }
25490                 ]
25491             };
25492         }
25493         
25494         var cfg = {
25495             tag: 'div',
25496             cls: 'nav-tabs-custom',
25497             cn: [
25498                 h,
25499                 {
25500                     tag: 'div',
25501                     cls: 'tab-content no-padding',
25502                     cn: []
25503                 }
25504             ]
25505         };
25506
25507         return  cfg;
25508     },
25509     initEvents : function()
25510     {
25511         //Roo.log('add add pane handler');
25512         this.on('addpane', this.onAddPane, this);
25513     },
25514      /**
25515      * Updates the box title
25516      * @param {String} html to set the title to.
25517      */
25518     setTitle : function(value)
25519     {
25520         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25521     },
25522     onAddPane : function(pane)
25523     {
25524         this.panes.push(pane);
25525         //Roo.log('addpane');
25526         //Roo.log(pane);
25527         // tabs are rendere left to right..
25528         if(!this.showtabs){
25529             return;
25530         }
25531         
25532         var ctr = this.el.select('.nav-tabs', true).first();
25533          
25534          
25535         var existing = ctr.select('.nav-tab',true);
25536         var qty = existing.getCount();;
25537         
25538         
25539         var tab = ctr.createChild({
25540             tag : 'li',
25541             cls : 'nav-tab' + (qty ? '' : ' active'),
25542             cn : [
25543                 {
25544                     tag : 'a',
25545                     href:'#',
25546                     html : pane.title
25547                 }
25548             ]
25549         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25550         pane.tab = tab;
25551         
25552         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25553         if (!qty) {
25554             pane.el.addClass('active');
25555         }
25556         
25557                 
25558     },
25559     onTabClick : function(ev,un,ob,pane)
25560     {
25561         //Roo.log('tab - prev default');
25562         ev.preventDefault();
25563         
25564         
25565         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25566         pane.tab.addClass('active');
25567         //Roo.log(pane.title);
25568         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25569         // technically we should have a deactivate event.. but maybe add later.
25570         // and it should not de-activate the selected tab...
25571         this.fireEvent('activatepane', pane);
25572         pane.el.addClass('active');
25573         pane.fireEvent('activate');
25574         
25575         
25576     },
25577     
25578     getActivePane : function()
25579     {
25580         var r = false;
25581         Roo.each(this.panes, function(p) {
25582             if(p.el.hasClass('active')){
25583                 r = p;
25584                 return false;
25585             }
25586             
25587             return;
25588         });
25589         
25590         return r;
25591     }
25592     
25593     
25594 });
25595
25596  
25597 /*
25598  * - LGPL
25599  *
25600  * Tab pane
25601  * 
25602  */
25603 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25604 /**
25605  * @class Roo.bootstrap.TabPane
25606  * @extends Roo.bootstrap.Component
25607  * Bootstrap TabPane class
25608  * @cfg {Boolean} active (false | true) Default false
25609  * @cfg {String} title title of panel
25610
25611  * 
25612  * @constructor
25613  * Create a new TabPane
25614  * @param {Object} config The config object
25615  */
25616
25617 Roo.bootstrap.dash.TabPane = function(config){
25618     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25619     
25620     this.addEvents({
25621         // raw events
25622         /**
25623          * @event activate
25624          * When a pane is activated
25625          * @param {Roo.bootstrap.dash.TabPane} pane
25626          */
25627         "activate" : true
25628          
25629     });
25630 };
25631
25632 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25633     
25634     active : false,
25635     title : '',
25636     
25637     // the tabBox that this is attached to.
25638     tab : false,
25639      
25640     getAutoCreate : function() 
25641     {
25642         var cfg = {
25643             tag: 'div',
25644             cls: 'tab-pane'
25645         };
25646         
25647         if(this.active){
25648             cfg.cls += ' active';
25649         }
25650         
25651         return cfg;
25652     },
25653     initEvents  : function()
25654     {
25655         //Roo.log('trigger add pane handler');
25656         this.parent().fireEvent('addpane', this)
25657     },
25658     
25659      /**
25660      * Updates the tab title 
25661      * @param {String} html to set the title to.
25662      */
25663     setTitle: function(str)
25664     {
25665         if (!this.tab) {
25666             return;
25667         }
25668         this.title = str;
25669         this.tab.select('a', true).first().dom.innerHTML = str;
25670         
25671     }
25672     
25673     
25674     
25675 });
25676
25677  
25678
25679
25680  /*
25681  * - LGPL
25682  *
25683  * menu
25684  * 
25685  */
25686 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25687
25688 /**
25689  * @class Roo.bootstrap.menu.Menu
25690  * @extends Roo.bootstrap.Component
25691  * Bootstrap Menu class - container for Menu
25692  * @cfg {String} html Text of the menu
25693  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25694  * @cfg {String} icon Font awesome icon
25695  * @cfg {String} pos Menu align to (top | bottom) default bottom
25696  * 
25697  * 
25698  * @constructor
25699  * Create a new Menu
25700  * @param {Object} config The config object
25701  */
25702
25703
25704 Roo.bootstrap.menu.Menu = function(config){
25705     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25706     
25707     this.addEvents({
25708         /**
25709          * @event beforeshow
25710          * Fires before this menu is displayed
25711          * @param {Roo.bootstrap.menu.Menu} this
25712          */
25713         beforeshow : true,
25714         /**
25715          * @event beforehide
25716          * Fires before this menu is hidden
25717          * @param {Roo.bootstrap.menu.Menu} this
25718          */
25719         beforehide : true,
25720         /**
25721          * @event show
25722          * Fires after this menu is displayed
25723          * @param {Roo.bootstrap.menu.Menu} this
25724          */
25725         show : true,
25726         /**
25727          * @event hide
25728          * Fires after this menu is hidden
25729          * @param {Roo.bootstrap.menu.Menu} this
25730          */
25731         hide : true,
25732         /**
25733          * @event click
25734          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25735          * @param {Roo.bootstrap.menu.Menu} this
25736          * @param {Roo.EventObject} e
25737          */
25738         click : true
25739     });
25740     
25741 };
25742
25743 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25744     
25745     submenu : false,
25746     html : '',
25747     weight : 'default',
25748     icon : false,
25749     pos : 'bottom',
25750     
25751     
25752     getChildContainer : function() {
25753         if(this.isSubMenu){
25754             return this.el;
25755         }
25756         
25757         return this.el.select('ul.dropdown-menu', true).first();  
25758     },
25759     
25760     getAutoCreate : function()
25761     {
25762         var text = [
25763             {
25764                 tag : 'span',
25765                 cls : 'roo-menu-text',
25766                 html : this.html
25767             }
25768         ];
25769         
25770         if(this.icon){
25771             text.unshift({
25772                 tag : 'i',
25773                 cls : 'fa ' + this.icon
25774             })
25775         }
25776         
25777         
25778         var cfg = {
25779             tag : 'div',
25780             cls : 'btn-group',
25781             cn : [
25782                 {
25783                     tag : 'button',
25784                     cls : 'dropdown-button btn btn-' + this.weight,
25785                     cn : text
25786                 },
25787                 {
25788                     tag : 'button',
25789                     cls : 'dropdown-toggle btn btn-' + this.weight,
25790                     cn : [
25791                         {
25792                             tag : 'span',
25793                             cls : 'caret'
25794                         }
25795                     ]
25796                 },
25797                 {
25798                     tag : 'ul',
25799                     cls : 'dropdown-menu'
25800                 }
25801             ]
25802             
25803         };
25804         
25805         if(this.pos == 'top'){
25806             cfg.cls += ' dropup';
25807         }
25808         
25809         if(this.isSubMenu){
25810             cfg = {
25811                 tag : 'ul',
25812                 cls : 'dropdown-menu'
25813             }
25814         }
25815         
25816         return cfg;
25817     },
25818     
25819     onRender : function(ct, position)
25820     {
25821         this.isSubMenu = ct.hasClass('dropdown-submenu');
25822         
25823         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25824     },
25825     
25826     initEvents : function() 
25827     {
25828         if(this.isSubMenu){
25829             return;
25830         }
25831         
25832         this.hidden = true;
25833         
25834         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25835         this.triggerEl.on('click', this.onTriggerPress, this);
25836         
25837         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25838         this.buttonEl.on('click', this.onClick, this);
25839         
25840     },
25841     
25842     list : function()
25843     {
25844         if(this.isSubMenu){
25845             return this.el;
25846         }
25847         
25848         return this.el.select('ul.dropdown-menu', true).first();
25849     },
25850     
25851     onClick : function(e)
25852     {
25853         this.fireEvent("click", this, e);
25854     },
25855     
25856     onTriggerPress  : function(e)
25857     {   
25858         if (this.isVisible()) {
25859             this.hide();
25860         } else {
25861             this.show();
25862         }
25863     },
25864     
25865     isVisible : function(){
25866         return !this.hidden;
25867     },
25868     
25869     show : function()
25870     {
25871         this.fireEvent("beforeshow", this);
25872         
25873         this.hidden = false;
25874         this.el.addClass('open');
25875         
25876         Roo.get(document).on("mouseup", this.onMouseUp, this);
25877         
25878         this.fireEvent("show", this);
25879         
25880         
25881     },
25882     
25883     hide : function()
25884     {
25885         this.fireEvent("beforehide", this);
25886         
25887         this.hidden = true;
25888         this.el.removeClass('open');
25889         
25890         Roo.get(document).un("mouseup", this.onMouseUp);
25891         
25892         this.fireEvent("hide", this);
25893     },
25894     
25895     onMouseUp : function()
25896     {
25897         this.hide();
25898     }
25899     
25900 });
25901
25902  
25903  /*
25904  * - LGPL
25905  *
25906  * menu item
25907  * 
25908  */
25909 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25910
25911 /**
25912  * @class Roo.bootstrap.menu.Item
25913  * @extends Roo.bootstrap.Component
25914  * Bootstrap MenuItem class
25915  * @cfg {Boolean} submenu (true | false) default false
25916  * @cfg {String} html text of the item
25917  * @cfg {String} href the link
25918  * @cfg {Boolean} disable (true | false) default false
25919  * @cfg {Boolean} preventDefault (true | false) default true
25920  * @cfg {String} icon Font awesome icon
25921  * @cfg {String} pos Submenu align to (left | right) default right 
25922  * 
25923  * 
25924  * @constructor
25925  * Create a new Item
25926  * @param {Object} config The config object
25927  */
25928
25929
25930 Roo.bootstrap.menu.Item = function(config){
25931     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25932     this.addEvents({
25933         /**
25934          * @event mouseover
25935          * Fires when the mouse is hovering over this menu
25936          * @param {Roo.bootstrap.menu.Item} this
25937          * @param {Roo.EventObject} e
25938          */
25939         mouseover : true,
25940         /**
25941          * @event mouseout
25942          * Fires when the mouse exits this menu
25943          * @param {Roo.bootstrap.menu.Item} this
25944          * @param {Roo.EventObject} e
25945          */
25946         mouseout : true,
25947         // raw events
25948         /**
25949          * @event click
25950          * The raw click event for the entire grid.
25951          * @param {Roo.EventObject} e
25952          */
25953         click : true
25954     });
25955 };
25956
25957 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25958     
25959     submenu : false,
25960     href : '',
25961     html : '',
25962     preventDefault: true,
25963     disable : false,
25964     icon : false,
25965     pos : 'right',
25966     
25967     getAutoCreate : function()
25968     {
25969         var text = [
25970             {
25971                 tag : 'span',
25972                 cls : 'roo-menu-item-text',
25973                 html : this.html
25974             }
25975         ];
25976         
25977         if(this.icon){
25978             text.unshift({
25979                 tag : 'i',
25980                 cls : 'fa ' + this.icon
25981             })
25982         }
25983         
25984         var cfg = {
25985             tag : 'li',
25986             cn : [
25987                 {
25988                     tag : 'a',
25989                     href : this.href || '#',
25990                     cn : text
25991                 }
25992             ]
25993         };
25994         
25995         if(this.disable){
25996             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25997         }
25998         
25999         if(this.submenu){
26000             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26001             
26002             if(this.pos == 'left'){
26003                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26004             }
26005         }
26006         
26007         return cfg;
26008     },
26009     
26010     initEvents : function() 
26011     {
26012         this.el.on('mouseover', this.onMouseOver, this);
26013         this.el.on('mouseout', this.onMouseOut, this);
26014         
26015         this.el.select('a', true).first().on('click', this.onClick, this);
26016         
26017     },
26018     
26019     onClick : function(e)
26020     {
26021         if(this.preventDefault){
26022             e.preventDefault();
26023         }
26024         
26025         this.fireEvent("click", this, e);
26026     },
26027     
26028     onMouseOver : function(e)
26029     {
26030         if(this.submenu && this.pos == 'left'){
26031             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26032         }
26033         
26034         this.fireEvent("mouseover", this, e);
26035     },
26036     
26037     onMouseOut : function(e)
26038     {
26039         this.fireEvent("mouseout", this, e);
26040     }
26041 });
26042
26043  
26044
26045  /*
26046  * - LGPL
26047  *
26048  * menu separator
26049  * 
26050  */
26051 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26052
26053 /**
26054  * @class Roo.bootstrap.menu.Separator
26055  * @extends Roo.bootstrap.Component
26056  * Bootstrap Separator class
26057  * 
26058  * @constructor
26059  * Create a new Separator
26060  * @param {Object} config The config object
26061  */
26062
26063
26064 Roo.bootstrap.menu.Separator = function(config){
26065     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26066 };
26067
26068 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26069     
26070     getAutoCreate : function(){
26071         var cfg = {
26072             tag : 'li',
26073             cls: 'divider'
26074         };
26075         
26076         return cfg;
26077     }
26078    
26079 });
26080
26081  
26082
26083  /*
26084  * - LGPL
26085  *
26086  * Tooltip
26087  * 
26088  */
26089
26090 /**
26091  * @class Roo.bootstrap.Tooltip
26092  * Bootstrap Tooltip class
26093  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26094  * to determine which dom element triggers the tooltip.
26095  * 
26096  * It needs to add support for additional attributes like tooltip-position
26097  * 
26098  * @constructor
26099  * Create a new Toolti
26100  * @param {Object} config The config object
26101  */
26102
26103 Roo.bootstrap.Tooltip = function(config){
26104     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26105     
26106     this.alignment = Roo.bootstrap.Tooltip.alignment;
26107     
26108     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26109         this.alignment = config.alignment;
26110     }
26111     
26112 };
26113
26114 Roo.apply(Roo.bootstrap.Tooltip, {
26115     /**
26116      * @function init initialize tooltip monitoring.
26117      * @static
26118      */
26119     currentEl : false,
26120     currentTip : false,
26121     currentRegion : false,
26122     
26123     //  init : delay?
26124     
26125     init : function()
26126     {
26127         Roo.get(document).on('mouseover', this.enter ,this);
26128         Roo.get(document).on('mouseout', this.leave, this);
26129          
26130         
26131         this.currentTip = new Roo.bootstrap.Tooltip();
26132     },
26133     
26134     enter : function(ev)
26135     {
26136         var dom = ev.getTarget();
26137         
26138         //Roo.log(['enter',dom]);
26139         var el = Roo.fly(dom);
26140         if (this.currentEl) {
26141             //Roo.log(dom);
26142             //Roo.log(this.currentEl);
26143             //Roo.log(this.currentEl.contains(dom));
26144             if (this.currentEl == el) {
26145                 return;
26146             }
26147             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26148                 return;
26149             }
26150
26151         }
26152         
26153         if (this.currentTip.el) {
26154             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26155         }    
26156         //Roo.log(ev);
26157         
26158         if(!el || el.dom == document){
26159             return;
26160         }
26161         
26162         var bindEl = el;
26163         
26164         // you can not look for children, as if el is the body.. then everythign is the child..
26165         if (!el.attr('tooltip')) { //
26166             if (!el.select("[tooltip]").elements.length) {
26167                 return;
26168             }
26169             // is the mouse over this child...?
26170             bindEl = el.select("[tooltip]").first();
26171             var xy = ev.getXY();
26172             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26173                 //Roo.log("not in region.");
26174                 return;
26175             }
26176             //Roo.log("child element over..");
26177             
26178         }
26179         this.currentEl = bindEl;
26180         this.currentTip.bind(bindEl);
26181         this.currentRegion = Roo.lib.Region.getRegion(dom);
26182         this.currentTip.enter();
26183         
26184     },
26185     leave : function(ev)
26186     {
26187         var dom = ev.getTarget();
26188         //Roo.log(['leave',dom]);
26189         if (!this.currentEl) {
26190             return;
26191         }
26192         
26193         
26194         if (dom != this.currentEl.dom) {
26195             return;
26196         }
26197         var xy = ev.getXY();
26198         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26199             return;
26200         }
26201         // only activate leave if mouse cursor is outside... bounding box..
26202         
26203         
26204         
26205         
26206         if (this.currentTip) {
26207             this.currentTip.leave();
26208         }
26209         //Roo.log('clear currentEl');
26210         this.currentEl = false;
26211         
26212         
26213     },
26214     alignment : {
26215         'left' : ['r-l', [-2,0], 'right'],
26216         'right' : ['l-r', [2,0], 'left'],
26217         'bottom' : ['t-b', [0,2], 'top'],
26218         'top' : [ 'b-t', [0,-2], 'bottom']
26219     }
26220     
26221 });
26222
26223
26224 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26225     
26226     
26227     bindEl : false,
26228     
26229     delay : null, // can be { show : 300 , hide: 500}
26230     
26231     timeout : null,
26232     
26233     hoverState : null, //???
26234     
26235     placement : 'bottom', 
26236     
26237     alignment : false,
26238     
26239     getAutoCreate : function(){
26240     
26241         var cfg = {
26242            cls : 'tooltip',
26243            role : 'tooltip',
26244            cn : [
26245                 {
26246                     cls : 'tooltip-arrow'
26247                 },
26248                 {
26249                     cls : 'tooltip-inner'
26250                 }
26251            ]
26252         };
26253         
26254         return cfg;
26255     },
26256     bind : function(el)
26257     {
26258         this.bindEl = el;
26259     },
26260       
26261     
26262     enter : function () {
26263        
26264         if (this.timeout != null) {
26265             clearTimeout(this.timeout);
26266         }
26267         
26268         this.hoverState = 'in';
26269          //Roo.log("enter - show");
26270         if (!this.delay || !this.delay.show) {
26271             this.show();
26272             return;
26273         }
26274         var _t = this;
26275         this.timeout = setTimeout(function () {
26276             if (_t.hoverState == 'in') {
26277                 _t.show();
26278             }
26279         }, this.delay.show);
26280     },
26281     leave : function()
26282     {
26283         clearTimeout(this.timeout);
26284     
26285         this.hoverState = 'out';
26286          if (!this.delay || !this.delay.hide) {
26287             this.hide();
26288             return;
26289         }
26290        
26291         var _t = this;
26292         this.timeout = setTimeout(function () {
26293             //Roo.log("leave - timeout");
26294             
26295             if (_t.hoverState == 'out') {
26296                 _t.hide();
26297                 Roo.bootstrap.Tooltip.currentEl = false;
26298             }
26299         }, delay);
26300     },
26301     
26302     show : function (msg)
26303     {
26304         if (!this.el) {
26305             this.render(document.body);
26306         }
26307         // set content.
26308         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26309         
26310         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26311         
26312         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26313         
26314         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26315         
26316         var placement = typeof this.placement == 'function' ?
26317             this.placement.call(this, this.el, on_el) :
26318             this.placement;
26319             
26320         var autoToken = /\s?auto?\s?/i;
26321         var autoPlace = autoToken.test(placement);
26322         if (autoPlace) {
26323             placement = placement.replace(autoToken, '') || 'top';
26324         }
26325         
26326         //this.el.detach()
26327         //this.el.setXY([0,0]);
26328         this.el.show();
26329         //this.el.dom.style.display='block';
26330         
26331         //this.el.appendTo(on_el);
26332         
26333         var p = this.getPosition();
26334         var box = this.el.getBox();
26335         
26336         if (autoPlace) {
26337             // fixme..
26338         }
26339         
26340         var align = this.alignment[placement];
26341         
26342         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26343         
26344         if(placement == 'top' || placement == 'bottom'){
26345             if(xy[0] < 0){
26346                 placement = 'right';
26347             }
26348             
26349             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26350                 placement = 'left';
26351             }
26352             
26353             var scroll = Roo.select('body', true).first().getScroll();
26354             
26355             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26356                 placement = 'top';
26357             }
26358             
26359             align = this.alignment[placement];
26360         }
26361         
26362         this.el.alignTo(this.bindEl, align[0],align[1]);
26363         //var arrow = this.el.select('.arrow',true).first();
26364         //arrow.set(align[2], 
26365         
26366         this.el.addClass(placement);
26367         
26368         this.el.addClass('in fade');
26369         
26370         this.hoverState = null;
26371         
26372         if (this.el.hasClass('fade')) {
26373             // fade it?
26374         }
26375         
26376     },
26377     hide : function()
26378     {
26379          
26380         if (!this.el) {
26381             return;
26382         }
26383         //this.el.setXY([0,0]);
26384         this.el.removeClass('in');
26385         //this.el.hide();
26386         
26387     }
26388     
26389 });
26390  
26391
26392  /*
26393  * - LGPL
26394  *
26395  * Location Picker
26396  * 
26397  */
26398
26399 /**
26400  * @class Roo.bootstrap.LocationPicker
26401  * @extends Roo.bootstrap.Component
26402  * Bootstrap LocationPicker class
26403  * @cfg {Number} latitude Position when init default 0
26404  * @cfg {Number} longitude Position when init default 0
26405  * @cfg {Number} zoom default 15
26406  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26407  * @cfg {Boolean} mapTypeControl default false
26408  * @cfg {Boolean} disableDoubleClickZoom default false
26409  * @cfg {Boolean} scrollwheel default true
26410  * @cfg {Boolean} streetViewControl default false
26411  * @cfg {Number} radius default 0
26412  * @cfg {String} locationName
26413  * @cfg {Boolean} draggable default true
26414  * @cfg {Boolean} enableAutocomplete default false
26415  * @cfg {Boolean} enableReverseGeocode default true
26416  * @cfg {String} markerTitle
26417  * 
26418  * @constructor
26419  * Create a new LocationPicker
26420  * @param {Object} config The config object
26421  */
26422
26423
26424 Roo.bootstrap.LocationPicker = function(config){
26425     
26426     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26427     
26428     this.addEvents({
26429         /**
26430          * @event initial
26431          * Fires when the picker initialized.
26432          * @param {Roo.bootstrap.LocationPicker} this
26433          * @param {Google Location} location
26434          */
26435         initial : true,
26436         /**
26437          * @event positionchanged
26438          * Fires when the picker position changed.
26439          * @param {Roo.bootstrap.LocationPicker} this
26440          * @param {Google Location} location
26441          */
26442         positionchanged : true,
26443         /**
26444          * @event resize
26445          * Fires when the map resize.
26446          * @param {Roo.bootstrap.LocationPicker} this
26447          */
26448         resize : true,
26449         /**
26450          * @event show
26451          * Fires when the map show.
26452          * @param {Roo.bootstrap.LocationPicker} this
26453          */
26454         show : true,
26455         /**
26456          * @event hide
26457          * Fires when the map hide.
26458          * @param {Roo.bootstrap.LocationPicker} this
26459          */
26460         hide : true,
26461         /**
26462          * @event mapClick
26463          * Fires when click the map.
26464          * @param {Roo.bootstrap.LocationPicker} this
26465          * @param {Map event} e
26466          */
26467         mapClick : true,
26468         /**
26469          * @event mapRightClick
26470          * Fires when right click the map.
26471          * @param {Roo.bootstrap.LocationPicker} this
26472          * @param {Map event} e
26473          */
26474         mapRightClick : true,
26475         /**
26476          * @event markerClick
26477          * Fires when click the marker.
26478          * @param {Roo.bootstrap.LocationPicker} this
26479          * @param {Map event} e
26480          */
26481         markerClick : true,
26482         /**
26483          * @event markerRightClick
26484          * Fires when right click the marker.
26485          * @param {Roo.bootstrap.LocationPicker} this
26486          * @param {Map event} e
26487          */
26488         markerRightClick : true,
26489         /**
26490          * @event OverlayViewDraw
26491          * Fires when OverlayView Draw
26492          * @param {Roo.bootstrap.LocationPicker} this
26493          */
26494         OverlayViewDraw : true,
26495         /**
26496          * @event OverlayViewOnAdd
26497          * Fires when OverlayView Draw
26498          * @param {Roo.bootstrap.LocationPicker} this
26499          */
26500         OverlayViewOnAdd : true,
26501         /**
26502          * @event OverlayViewOnRemove
26503          * Fires when OverlayView Draw
26504          * @param {Roo.bootstrap.LocationPicker} this
26505          */
26506         OverlayViewOnRemove : true,
26507         /**
26508          * @event OverlayViewShow
26509          * Fires when OverlayView Draw
26510          * @param {Roo.bootstrap.LocationPicker} this
26511          * @param {Pixel} cpx
26512          */
26513         OverlayViewShow : true,
26514         /**
26515          * @event OverlayViewHide
26516          * Fires when OverlayView Draw
26517          * @param {Roo.bootstrap.LocationPicker} this
26518          */
26519         OverlayViewHide : true,
26520         /**
26521          * @event loadexception
26522          * Fires when load google lib failed.
26523          * @param {Roo.bootstrap.LocationPicker} this
26524          */
26525         loadexception : true
26526     });
26527         
26528 };
26529
26530 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26531     
26532     gMapContext: false,
26533     
26534     latitude: 0,
26535     longitude: 0,
26536     zoom: 15,
26537     mapTypeId: false,
26538     mapTypeControl: false,
26539     disableDoubleClickZoom: false,
26540     scrollwheel: true,
26541     streetViewControl: false,
26542     radius: 0,
26543     locationName: '',
26544     draggable: true,
26545     enableAutocomplete: false,
26546     enableReverseGeocode: true,
26547     markerTitle: '',
26548     
26549     getAutoCreate: function()
26550     {
26551
26552         var cfg = {
26553             tag: 'div',
26554             cls: 'roo-location-picker'
26555         };
26556         
26557         return cfg
26558     },
26559     
26560     initEvents: function(ct, position)
26561     {       
26562         if(!this.el.getWidth() || this.isApplied()){
26563             return;
26564         }
26565         
26566         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26567         
26568         this.initial();
26569     },
26570     
26571     initial: function()
26572     {
26573         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26574             this.fireEvent('loadexception', this);
26575             return;
26576         }
26577         
26578         if(!this.mapTypeId){
26579             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26580         }
26581         
26582         this.gMapContext = this.GMapContext();
26583         
26584         this.initOverlayView();
26585         
26586         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26587         
26588         var _this = this;
26589                 
26590         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26591             _this.setPosition(_this.gMapContext.marker.position);
26592         });
26593         
26594         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26595             _this.fireEvent('mapClick', this, event);
26596             
26597         });
26598
26599         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26600             _this.fireEvent('mapRightClick', this, event);
26601             
26602         });
26603         
26604         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26605             _this.fireEvent('markerClick', this, event);
26606             
26607         });
26608
26609         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26610             _this.fireEvent('markerRightClick', this, event);
26611             
26612         });
26613         
26614         this.setPosition(this.gMapContext.location);
26615         
26616         this.fireEvent('initial', this, this.gMapContext.location);
26617     },
26618     
26619     initOverlayView: function()
26620     {
26621         var _this = this;
26622         
26623         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26624             
26625             draw: function()
26626             {
26627                 _this.fireEvent('OverlayViewDraw', _this);
26628             },
26629             
26630             onAdd: function()
26631             {
26632                 _this.fireEvent('OverlayViewOnAdd', _this);
26633             },
26634             
26635             onRemove: function()
26636             {
26637                 _this.fireEvent('OverlayViewOnRemove', _this);
26638             },
26639             
26640             show: function(cpx)
26641             {
26642                 _this.fireEvent('OverlayViewShow', _this, cpx);
26643             },
26644             
26645             hide: function()
26646             {
26647                 _this.fireEvent('OverlayViewHide', _this);
26648             }
26649             
26650         });
26651     },
26652     
26653     fromLatLngToContainerPixel: function(event)
26654     {
26655         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26656     },
26657     
26658     isApplied: function() 
26659     {
26660         return this.getGmapContext() == false ? false : true;
26661     },
26662     
26663     getGmapContext: function() 
26664     {
26665         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26666     },
26667     
26668     GMapContext: function() 
26669     {
26670         var position = new google.maps.LatLng(this.latitude, this.longitude);
26671         
26672         var _map = new google.maps.Map(this.el.dom, {
26673             center: position,
26674             zoom: this.zoom,
26675             mapTypeId: this.mapTypeId,
26676             mapTypeControl: this.mapTypeControl,
26677             disableDoubleClickZoom: this.disableDoubleClickZoom,
26678             scrollwheel: this.scrollwheel,
26679             streetViewControl: this.streetViewControl,
26680             locationName: this.locationName,
26681             draggable: this.draggable,
26682             enableAutocomplete: this.enableAutocomplete,
26683             enableReverseGeocode: this.enableReverseGeocode
26684         });
26685         
26686         var _marker = new google.maps.Marker({
26687             position: position,
26688             map: _map,
26689             title: this.markerTitle,
26690             draggable: this.draggable
26691         });
26692         
26693         return {
26694             map: _map,
26695             marker: _marker,
26696             circle: null,
26697             location: position,
26698             radius: this.radius,
26699             locationName: this.locationName,
26700             addressComponents: {
26701                 formatted_address: null,
26702                 addressLine1: null,
26703                 addressLine2: null,
26704                 streetName: null,
26705                 streetNumber: null,
26706                 city: null,
26707                 district: null,
26708                 state: null,
26709                 stateOrProvince: null
26710             },
26711             settings: this,
26712             domContainer: this.el.dom,
26713             geodecoder: new google.maps.Geocoder()
26714         };
26715     },
26716     
26717     drawCircle: function(center, radius, options) 
26718     {
26719         if (this.gMapContext.circle != null) {
26720             this.gMapContext.circle.setMap(null);
26721         }
26722         if (radius > 0) {
26723             radius *= 1;
26724             options = Roo.apply({}, options, {
26725                 strokeColor: "#0000FF",
26726                 strokeOpacity: .35,
26727                 strokeWeight: 2,
26728                 fillColor: "#0000FF",
26729                 fillOpacity: .2
26730             });
26731             
26732             options.map = this.gMapContext.map;
26733             options.radius = radius;
26734             options.center = center;
26735             this.gMapContext.circle = new google.maps.Circle(options);
26736             return this.gMapContext.circle;
26737         }
26738         
26739         return null;
26740     },
26741     
26742     setPosition: function(location) 
26743     {
26744         this.gMapContext.location = location;
26745         this.gMapContext.marker.setPosition(location);
26746         this.gMapContext.map.panTo(location);
26747         this.drawCircle(location, this.gMapContext.radius, {});
26748         
26749         var _this = this;
26750         
26751         if (this.gMapContext.settings.enableReverseGeocode) {
26752             this.gMapContext.geodecoder.geocode({
26753                 latLng: this.gMapContext.location
26754             }, function(results, status) {
26755                 
26756                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26757                     _this.gMapContext.locationName = results[0].formatted_address;
26758                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26759                     
26760                     _this.fireEvent('positionchanged', this, location);
26761                 }
26762             });
26763             
26764             return;
26765         }
26766         
26767         this.fireEvent('positionchanged', this, location);
26768     },
26769     
26770     resize: function()
26771     {
26772         google.maps.event.trigger(this.gMapContext.map, "resize");
26773         
26774         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26775         
26776         this.fireEvent('resize', this);
26777     },
26778     
26779     setPositionByLatLng: function(latitude, longitude)
26780     {
26781         this.setPosition(new google.maps.LatLng(latitude, longitude));
26782     },
26783     
26784     getCurrentPosition: function() 
26785     {
26786         return {
26787             latitude: this.gMapContext.location.lat(),
26788             longitude: this.gMapContext.location.lng()
26789         };
26790     },
26791     
26792     getAddressName: function() 
26793     {
26794         return this.gMapContext.locationName;
26795     },
26796     
26797     getAddressComponents: function() 
26798     {
26799         return this.gMapContext.addressComponents;
26800     },
26801     
26802     address_component_from_google_geocode: function(address_components) 
26803     {
26804         var result = {};
26805         
26806         for (var i = 0; i < address_components.length; i++) {
26807             var component = address_components[i];
26808             if (component.types.indexOf("postal_code") >= 0) {
26809                 result.postalCode = component.short_name;
26810             } else if (component.types.indexOf("street_number") >= 0) {
26811                 result.streetNumber = component.short_name;
26812             } else if (component.types.indexOf("route") >= 0) {
26813                 result.streetName = component.short_name;
26814             } else if (component.types.indexOf("neighborhood") >= 0) {
26815                 result.city = component.short_name;
26816             } else if (component.types.indexOf("locality") >= 0) {
26817                 result.city = component.short_name;
26818             } else if (component.types.indexOf("sublocality") >= 0) {
26819                 result.district = component.short_name;
26820             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26821                 result.stateOrProvince = component.short_name;
26822             } else if (component.types.indexOf("country") >= 0) {
26823                 result.country = component.short_name;
26824             }
26825         }
26826         
26827         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26828         result.addressLine2 = "";
26829         return result;
26830     },
26831     
26832     setZoomLevel: function(zoom)
26833     {
26834         this.gMapContext.map.setZoom(zoom);
26835     },
26836     
26837     show: function()
26838     {
26839         if(!this.el){
26840             return;
26841         }
26842         
26843         this.el.show();
26844         
26845         this.resize();
26846         
26847         this.fireEvent('show', this);
26848     },
26849     
26850     hide: function()
26851     {
26852         if(!this.el){
26853             return;
26854         }
26855         
26856         this.el.hide();
26857         
26858         this.fireEvent('hide', this);
26859     }
26860     
26861 });
26862
26863 Roo.apply(Roo.bootstrap.LocationPicker, {
26864     
26865     OverlayView : function(map, options)
26866     {
26867         options = options || {};
26868         
26869         this.setMap(map);
26870     }
26871     
26872     
26873 });/*
26874  * - LGPL
26875  *
26876  * Alert
26877  * 
26878  */
26879
26880 /**
26881  * @class Roo.bootstrap.Alert
26882  * @extends Roo.bootstrap.Component
26883  * Bootstrap Alert class
26884  * @cfg {String} title The title of alert
26885  * @cfg {String} html The content of alert
26886  * @cfg {String} weight (  success | info | warning | danger )
26887  * @cfg {String} faicon font-awesomeicon
26888  * 
26889  * @constructor
26890  * Create a new alert
26891  * @param {Object} config The config object
26892  */
26893
26894
26895 Roo.bootstrap.Alert = function(config){
26896     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26897     
26898 };
26899
26900 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26901     
26902     title: '',
26903     html: '',
26904     weight: false,
26905     faicon: false,
26906     
26907     getAutoCreate : function()
26908     {
26909         
26910         var cfg = {
26911             tag : 'div',
26912             cls : 'alert',
26913             cn : [
26914                 {
26915                     tag : 'i',
26916                     cls : 'roo-alert-icon'
26917                     
26918                 },
26919                 {
26920                     tag : 'b',
26921                     cls : 'roo-alert-title',
26922                     html : this.title
26923                 },
26924                 {
26925                     tag : 'span',
26926                     cls : 'roo-alert-text',
26927                     html : this.html
26928                 }
26929             ]
26930         };
26931         
26932         if(this.faicon){
26933             cfg.cn[0].cls += ' fa ' + this.faicon;
26934         }
26935         
26936         if(this.weight){
26937             cfg.cls += ' alert-' + this.weight;
26938         }
26939         
26940         return cfg;
26941     },
26942     
26943     initEvents: function() 
26944     {
26945         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26946     },
26947     
26948     setTitle : function(str)
26949     {
26950         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26951     },
26952     
26953     setText : function(str)
26954     {
26955         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26956     },
26957     
26958     setWeight : function(weight)
26959     {
26960         if(this.weight){
26961             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26962         }
26963         
26964         this.weight = weight;
26965         
26966         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26967     },
26968     
26969     setIcon : function(icon)
26970     {
26971         if(this.faicon){
26972             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26973         }
26974         
26975         this.faicon = icon;
26976         
26977         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26978     },
26979     
26980     hide: function() 
26981     {
26982         this.el.hide();   
26983     },
26984     
26985     show: function() 
26986     {  
26987         this.el.show();   
26988     }
26989     
26990 });
26991
26992  
26993 /*
26994 * Licence: LGPL
26995 */
26996
26997 /**
26998  * @class Roo.bootstrap.UploadCropbox
26999  * @extends Roo.bootstrap.Component
27000  * Bootstrap UploadCropbox class
27001  * @cfg {String} emptyText show when image has been loaded
27002  * @cfg {String} rotateNotify show when image too small to rotate
27003  * @cfg {Number} errorTimeout default 3000
27004  * @cfg {Number} minWidth default 300
27005  * @cfg {Number} minHeight default 300
27006  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27007  * @cfg {Boolean} isDocument (true|false) default false
27008  * @cfg {String} url action url
27009  * @cfg {String} paramName default 'imageUpload'
27010  * @cfg {String} method default POST
27011  * @cfg {Boolean} loadMask (true|false) default true
27012  * @cfg {Boolean} loadingText default 'Loading...'
27013  * 
27014  * @constructor
27015  * Create a new UploadCropbox
27016  * @param {Object} config The config object
27017  */
27018
27019 Roo.bootstrap.UploadCropbox = function(config){
27020     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27021     
27022     this.addEvents({
27023         /**
27024          * @event beforeselectfile
27025          * Fire before select file
27026          * @param {Roo.bootstrap.UploadCropbox} this
27027          */
27028         "beforeselectfile" : true,
27029         /**
27030          * @event initial
27031          * Fire after initEvent
27032          * @param {Roo.bootstrap.UploadCropbox} this
27033          */
27034         "initial" : true,
27035         /**
27036          * @event crop
27037          * Fire after initEvent
27038          * @param {Roo.bootstrap.UploadCropbox} this
27039          * @param {String} data
27040          */
27041         "crop" : true,
27042         /**
27043          * @event prepare
27044          * Fire when preparing the file data
27045          * @param {Roo.bootstrap.UploadCropbox} this
27046          * @param {Object} file
27047          */
27048         "prepare" : true,
27049         /**
27050          * @event exception
27051          * Fire when get exception
27052          * @param {Roo.bootstrap.UploadCropbox} this
27053          * @param {XMLHttpRequest} xhr
27054          */
27055         "exception" : true,
27056         /**
27057          * @event beforeloadcanvas
27058          * Fire before load the canvas
27059          * @param {Roo.bootstrap.UploadCropbox} this
27060          * @param {String} src
27061          */
27062         "beforeloadcanvas" : true,
27063         /**
27064          * @event trash
27065          * Fire when trash image
27066          * @param {Roo.bootstrap.UploadCropbox} this
27067          */
27068         "trash" : true,
27069         /**
27070          * @event download
27071          * Fire when download the image
27072          * @param {Roo.bootstrap.UploadCropbox} this
27073          */
27074         "download" : true,
27075         /**
27076          * @event footerbuttonclick
27077          * Fire when footerbuttonclick
27078          * @param {Roo.bootstrap.UploadCropbox} this
27079          * @param {String} type
27080          */
27081         "footerbuttonclick" : true,
27082         /**
27083          * @event resize
27084          * Fire when resize
27085          * @param {Roo.bootstrap.UploadCropbox} this
27086          */
27087         "resize" : true,
27088         /**
27089          * @event rotate
27090          * Fire when rotate the image
27091          * @param {Roo.bootstrap.UploadCropbox} this
27092          * @param {String} pos
27093          */
27094         "rotate" : true,
27095         /**
27096          * @event inspect
27097          * Fire when inspect the file
27098          * @param {Roo.bootstrap.UploadCropbox} this
27099          * @param {Object} file
27100          */
27101         "inspect" : true,
27102         /**
27103          * @event upload
27104          * Fire when xhr upload the file
27105          * @param {Roo.bootstrap.UploadCropbox} this
27106          * @param {Object} data
27107          */
27108         "upload" : true,
27109         /**
27110          * @event arrange
27111          * Fire when arrange the file data
27112          * @param {Roo.bootstrap.UploadCropbox} this
27113          * @param {Object} formData
27114          */
27115         "arrange" : true
27116     });
27117     
27118     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27119 };
27120
27121 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27122     
27123     emptyText : 'Click to upload image',
27124     rotateNotify : 'Image is too small to rotate',
27125     errorTimeout : 3000,
27126     scale : 0,
27127     baseScale : 1,
27128     rotate : 0,
27129     dragable : false,
27130     pinching : false,
27131     mouseX : 0,
27132     mouseY : 0,
27133     cropData : false,
27134     minWidth : 300,
27135     minHeight : 300,
27136     file : false,
27137     exif : {},
27138     baseRotate : 1,
27139     cropType : 'image/jpeg',
27140     buttons : false,
27141     canvasLoaded : false,
27142     isDocument : false,
27143     method : 'POST',
27144     paramName : 'imageUpload',
27145     loadMask : true,
27146     loadingText : 'Loading...',
27147     maskEl : false,
27148     
27149     getAutoCreate : function()
27150     {
27151         var cfg = {
27152             tag : 'div',
27153             cls : 'roo-upload-cropbox',
27154             cn : [
27155                 {
27156                     tag : 'input',
27157                     cls : 'roo-upload-cropbox-selector',
27158                     type : 'file'
27159                 },
27160                 {
27161                     tag : 'div',
27162                     cls : 'roo-upload-cropbox-body',
27163                     style : 'cursor:pointer',
27164                     cn : [
27165                         {
27166                             tag : 'div',
27167                             cls : 'roo-upload-cropbox-preview'
27168                         },
27169                         {
27170                             tag : 'div',
27171                             cls : 'roo-upload-cropbox-thumb'
27172                         },
27173                         {
27174                             tag : 'div',
27175                             cls : 'roo-upload-cropbox-empty-notify',
27176                             html : this.emptyText
27177                         },
27178                         {
27179                             tag : 'div',
27180                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27181                             html : this.rotateNotify
27182                         }
27183                     ]
27184                 },
27185                 {
27186                     tag : 'div',
27187                     cls : 'roo-upload-cropbox-footer',
27188                     cn : {
27189                         tag : 'div',
27190                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27191                         cn : []
27192                     }
27193                 }
27194             ]
27195         };
27196         
27197         return cfg;
27198     },
27199     
27200     onRender : function(ct, position)
27201     {
27202         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27203         
27204         if (this.buttons.length) {
27205             
27206             Roo.each(this.buttons, function(bb) {
27207                 
27208                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27209                 
27210                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27211                 
27212             }, this);
27213         }
27214         
27215         if(this.loadMask){
27216             this.maskEl = this.el;
27217         }
27218     },
27219     
27220     initEvents : function()
27221     {
27222         this.urlAPI = (window.createObjectURL && window) || 
27223                                 (window.URL && URL.revokeObjectURL && URL) || 
27224                                 (window.webkitURL && webkitURL);
27225                         
27226         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27227         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27228         
27229         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27230         this.selectorEl.hide();
27231         
27232         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27233         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27234         
27235         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27236         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27237         this.thumbEl.hide();
27238         
27239         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27240         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27241         
27242         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27243         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27244         this.errorEl.hide();
27245         
27246         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27247         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27248         this.footerEl.hide();
27249         
27250         this.setThumbBoxSize();
27251         
27252         this.bind();
27253         
27254         this.resize();
27255         
27256         this.fireEvent('initial', this);
27257     },
27258
27259     bind : function()
27260     {
27261         var _this = this;
27262         
27263         window.addEventListener("resize", function() { _this.resize(); } );
27264         
27265         this.bodyEl.on('click', this.beforeSelectFile, this);
27266         
27267         if(Roo.isTouch){
27268             this.bodyEl.on('touchstart', this.onTouchStart, this);
27269             this.bodyEl.on('touchmove', this.onTouchMove, this);
27270             this.bodyEl.on('touchend', this.onTouchEnd, this);
27271         }
27272         
27273         if(!Roo.isTouch){
27274             this.bodyEl.on('mousedown', this.onMouseDown, this);
27275             this.bodyEl.on('mousemove', this.onMouseMove, this);
27276             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27277             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27278             Roo.get(document).on('mouseup', this.onMouseUp, this);
27279         }
27280         
27281         this.selectorEl.on('change', this.onFileSelected, this);
27282     },
27283     
27284     reset : function()
27285     {    
27286         this.scale = 0;
27287         this.baseScale = 1;
27288         this.rotate = 0;
27289         this.baseRotate = 1;
27290         this.dragable = false;
27291         this.pinching = false;
27292         this.mouseX = 0;
27293         this.mouseY = 0;
27294         this.cropData = false;
27295         this.notifyEl.dom.innerHTML = this.emptyText;
27296         
27297         this.selectorEl.dom.value = '';
27298         
27299     },
27300     
27301     resize : function()
27302     {
27303         if(this.fireEvent('resize', this) != false){
27304             this.setThumbBoxPosition();
27305             this.setCanvasPosition();
27306         }
27307     },
27308     
27309     onFooterButtonClick : function(e, el, o, type)
27310     {
27311         switch (type) {
27312             case 'rotate-left' :
27313                 this.onRotateLeft(e);
27314                 break;
27315             case 'rotate-right' :
27316                 this.onRotateRight(e);
27317                 break;
27318             case 'picture' :
27319                 this.beforeSelectFile(e);
27320                 break;
27321             case 'trash' :
27322                 this.trash(e);
27323                 break;
27324             case 'crop' :
27325                 this.crop(e);
27326                 break;
27327             case 'download' :
27328                 this.download(e);
27329                 break;
27330             default :
27331                 break;
27332         }
27333         
27334         this.fireEvent('footerbuttonclick', this, type);
27335     },
27336     
27337     beforeSelectFile : function(e)
27338     {
27339         e.preventDefault();
27340         
27341         if(this.fireEvent('beforeselectfile', this) != false){
27342             this.selectorEl.dom.click();
27343         }
27344     },
27345     
27346     onFileSelected : function(e)
27347     {
27348         e.preventDefault();
27349         
27350         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27351             return;
27352         }
27353         
27354         var file = this.selectorEl.dom.files[0];
27355         
27356         if(this.fireEvent('inspect', this, file) != false){
27357             this.prepare(file);
27358         }
27359         
27360     },
27361     
27362     trash : function(e)
27363     {
27364         this.fireEvent('trash', this);
27365     },
27366     
27367     download : function(e)
27368     {
27369         this.fireEvent('download', this);
27370     },
27371     
27372     loadCanvas : function(src)
27373     {   
27374         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27375             
27376             this.reset();
27377             
27378             this.imageEl = document.createElement('img');
27379             
27380             var _this = this;
27381             
27382             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27383             
27384             this.imageEl.src = src;
27385         }
27386     },
27387     
27388     onLoadCanvas : function()
27389     {   
27390         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27391         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27392         
27393         this.bodyEl.un('click', this.beforeSelectFile, this);
27394         
27395         this.notifyEl.hide();
27396         this.thumbEl.show();
27397         this.footerEl.show();
27398         
27399         this.baseRotateLevel();
27400         
27401         if(this.isDocument){
27402             this.setThumbBoxSize();
27403         }
27404         
27405         this.setThumbBoxPosition();
27406         
27407         this.baseScaleLevel();
27408         
27409         this.draw();
27410         
27411         this.resize();
27412         
27413         this.canvasLoaded = true;
27414         
27415         if(this.loadMask){
27416             this.maskEl.unmask();
27417         }
27418         
27419     },
27420     
27421     setCanvasPosition : function()
27422     {   
27423         if(!this.canvasEl){
27424             return;
27425         }
27426         
27427         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27428         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27429         
27430         this.previewEl.setLeft(pw);
27431         this.previewEl.setTop(ph);
27432         
27433     },
27434     
27435     onMouseDown : function(e)
27436     {   
27437         e.stopEvent();
27438         
27439         this.dragable = true;
27440         this.pinching = false;
27441         
27442         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27443             this.dragable = false;
27444             return;
27445         }
27446         
27447         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27448         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27449         
27450     },
27451     
27452     onMouseMove : function(e)
27453     {   
27454         e.stopEvent();
27455         
27456         if(!this.canvasLoaded){
27457             return;
27458         }
27459         
27460         if (!this.dragable){
27461             return;
27462         }
27463         
27464         var minX = Math.ceil(this.thumbEl.getLeft(true));
27465         var minY = Math.ceil(this.thumbEl.getTop(true));
27466         
27467         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27468         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27469         
27470         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27471         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27472         
27473         x = x - this.mouseX;
27474         y = y - this.mouseY;
27475         
27476         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27477         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27478         
27479         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27480         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27481         
27482         this.previewEl.setLeft(bgX);
27483         this.previewEl.setTop(bgY);
27484         
27485         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27486         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27487     },
27488     
27489     onMouseUp : function(e)
27490     {   
27491         e.stopEvent();
27492         
27493         this.dragable = false;
27494     },
27495     
27496     onMouseWheel : function(e)
27497     {   
27498         e.stopEvent();
27499         
27500         this.startScale = this.scale;
27501         
27502         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27503         
27504         if(!this.zoomable()){
27505             this.scale = this.startScale;
27506             return;
27507         }
27508         
27509         this.draw();
27510         
27511         return;
27512     },
27513     
27514     zoomable : function()
27515     {
27516         var minScale = this.thumbEl.getWidth() / this.minWidth;
27517         
27518         if(this.minWidth < this.minHeight){
27519             minScale = this.thumbEl.getHeight() / this.minHeight;
27520         }
27521         
27522         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27523         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27524         
27525         if(
27526                 this.isDocument &&
27527                 (this.rotate == 0 || this.rotate == 180) && 
27528                 (
27529                     width > this.imageEl.OriginWidth || 
27530                     height > this.imageEl.OriginHeight ||
27531                     (width < this.minWidth && height < this.minHeight)
27532                 )
27533         ){
27534             return false;
27535         }
27536         
27537         if(
27538                 this.isDocument &&
27539                 (this.rotate == 90 || this.rotate == 270) && 
27540                 (
27541                     width > this.imageEl.OriginWidth || 
27542                     height > this.imageEl.OriginHeight ||
27543                     (width < this.minHeight && height < this.minWidth)
27544                 )
27545         ){
27546             return false;
27547         }
27548         
27549         if(
27550                 !this.isDocument &&
27551                 (this.rotate == 0 || this.rotate == 180) && 
27552                 (
27553                     width < this.minWidth || 
27554                     width > this.imageEl.OriginWidth || 
27555                     height < this.minHeight || 
27556                     height > this.imageEl.OriginHeight
27557                 )
27558         ){
27559             return false;
27560         }
27561         
27562         if(
27563                 !this.isDocument &&
27564                 (this.rotate == 90 || this.rotate == 270) && 
27565                 (
27566                     width < this.minHeight || 
27567                     width > this.imageEl.OriginWidth || 
27568                     height < this.minWidth || 
27569                     height > this.imageEl.OriginHeight
27570                 )
27571         ){
27572             return false;
27573         }
27574         
27575         return true;
27576         
27577     },
27578     
27579     onRotateLeft : function(e)
27580     {   
27581         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27582             
27583             var minScale = this.thumbEl.getWidth() / this.minWidth;
27584             
27585             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27586             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27587             
27588             this.startScale = this.scale;
27589             
27590             while (this.getScaleLevel() < minScale){
27591             
27592                 this.scale = this.scale + 1;
27593                 
27594                 if(!this.zoomable()){
27595                     break;
27596                 }
27597                 
27598                 if(
27599                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27600                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27601                 ){
27602                     continue;
27603                 }
27604                 
27605                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27606
27607                 this.draw();
27608                 
27609                 return;
27610             }
27611             
27612             this.scale = this.startScale;
27613             
27614             this.onRotateFail();
27615             
27616             return false;
27617         }
27618         
27619         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27620
27621         if(this.isDocument){
27622             this.setThumbBoxSize();
27623             this.setThumbBoxPosition();
27624             this.setCanvasPosition();
27625         }
27626         
27627         this.draw();
27628         
27629         this.fireEvent('rotate', this, 'left');
27630         
27631     },
27632     
27633     onRotateRight : function(e)
27634     {
27635         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27636             
27637             var minScale = this.thumbEl.getWidth() / this.minWidth;
27638         
27639             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27640             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27641             
27642             this.startScale = this.scale;
27643             
27644             while (this.getScaleLevel() < minScale){
27645             
27646                 this.scale = this.scale + 1;
27647                 
27648                 if(!this.zoomable()){
27649                     break;
27650                 }
27651                 
27652                 if(
27653                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27654                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27655                 ){
27656                     continue;
27657                 }
27658                 
27659                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27660
27661                 this.draw();
27662                 
27663                 return;
27664             }
27665             
27666             this.scale = this.startScale;
27667             
27668             this.onRotateFail();
27669             
27670             return false;
27671         }
27672         
27673         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27674
27675         if(this.isDocument){
27676             this.setThumbBoxSize();
27677             this.setThumbBoxPosition();
27678             this.setCanvasPosition();
27679         }
27680         
27681         this.draw();
27682         
27683         this.fireEvent('rotate', this, 'right');
27684     },
27685     
27686     onRotateFail : function()
27687     {
27688         this.errorEl.show(true);
27689         
27690         var _this = this;
27691         
27692         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27693     },
27694     
27695     draw : function()
27696     {
27697         this.previewEl.dom.innerHTML = '';
27698         
27699         var canvasEl = document.createElement("canvas");
27700         
27701         var contextEl = canvasEl.getContext("2d");
27702         
27703         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27704         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27705         var center = this.imageEl.OriginWidth / 2;
27706         
27707         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27708             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27709             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27710             center = this.imageEl.OriginHeight / 2;
27711         }
27712         
27713         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27714         
27715         contextEl.translate(center, center);
27716         contextEl.rotate(this.rotate * Math.PI / 180);
27717
27718         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27719         
27720         this.canvasEl = document.createElement("canvas");
27721         
27722         this.contextEl = this.canvasEl.getContext("2d");
27723         
27724         switch (this.rotate) {
27725             case 0 :
27726                 
27727                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27728                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27729                 
27730                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27731                 
27732                 break;
27733             case 90 : 
27734                 
27735                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27736                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27737                 
27738                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27739                     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);
27740                     break;
27741                 }
27742                 
27743                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27744                 
27745                 break;
27746             case 180 :
27747                 
27748                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27749                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27750                 
27751                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27752                     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);
27753                     break;
27754                 }
27755                 
27756                 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);
27757                 
27758                 break;
27759             case 270 :
27760                 
27761                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27762                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27763         
27764                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27765                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27766                     break;
27767                 }
27768                 
27769                 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);
27770                 
27771                 break;
27772             default : 
27773                 break;
27774         }
27775         
27776         this.previewEl.appendChild(this.canvasEl);
27777         
27778         this.setCanvasPosition();
27779     },
27780     
27781     crop : function()
27782     {
27783         if(!this.canvasLoaded){
27784             return;
27785         }
27786         
27787         var imageCanvas = document.createElement("canvas");
27788         
27789         var imageContext = imageCanvas.getContext("2d");
27790         
27791         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27792         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27793         
27794         var center = imageCanvas.width / 2;
27795         
27796         imageContext.translate(center, center);
27797         
27798         imageContext.rotate(this.rotate * Math.PI / 180);
27799         
27800         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27801         
27802         var canvas = document.createElement("canvas");
27803         
27804         var context = canvas.getContext("2d");
27805                 
27806         canvas.width = this.minWidth;
27807         canvas.height = this.minHeight;
27808
27809         switch (this.rotate) {
27810             case 0 :
27811                 
27812                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27813                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27814                 
27815                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27816                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27817                 
27818                 var targetWidth = this.minWidth - 2 * x;
27819                 var targetHeight = this.minHeight - 2 * y;
27820                 
27821                 var scale = 1;
27822                 
27823                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27824                     scale = targetWidth / width;
27825                 }
27826                 
27827                 if(x > 0 && y == 0){
27828                     scale = targetHeight / height;
27829                 }
27830                 
27831                 if(x > 0 && y > 0){
27832                     scale = targetWidth / width;
27833                     
27834                     if(width < height){
27835                         scale = targetHeight / height;
27836                     }
27837                 }
27838                 
27839                 context.scale(scale, scale);
27840                 
27841                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27842                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27843
27844                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27845                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27846
27847                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27848                 
27849                 break;
27850             case 90 : 
27851                 
27852                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27853                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27854                 
27855                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27856                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27857                 
27858                 var targetWidth = this.minWidth - 2 * x;
27859                 var targetHeight = this.minHeight - 2 * y;
27860                 
27861                 var scale = 1;
27862                 
27863                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27864                     scale = targetWidth / width;
27865                 }
27866                 
27867                 if(x > 0 && y == 0){
27868                     scale = targetHeight / height;
27869                 }
27870                 
27871                 if(x > 0 && y > 0){
27872                     scale = targetWidth / width;
27873                     
27874                     if(width < height){
27875                         scale = targetHeight / height;
27876                     }
27877                 }
27878                 
27879                 context.scale(scale, scale);
27880                 
27881                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27882                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27883
27884                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27885                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27886                 
27887                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27888                 
27889                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27890                 
27891                 break;
27892             case 180 :
27893                 
27894                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27895                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27896                 
27897                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27898                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27899                 
27900                 var targetWidth = this.minWidth - 2 * x;
27901                 var targetHeight = this.minHeight - 2 * y;
27902                 
27903                 var scale = 1;
27904                 
27905                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27906                     scale = targetWidth / width;
27907                 }
27908                 
27909                 if(x > 0 && y == 0){
27910                     scale = targetHeight / height;
27911                 }
27912                 
27913                 if(x > 0 && y > 0){
27914                     scale = targetWidth / width;
27915                     
27916                     if(width < height){
27917                         scale = targetHeight / height;
27918                     }
27919                 }
27920                 
27921                 context.scale(scale, scale);
27922                 
27923                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27924                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27925
27926                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27927                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27928
27929                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27930                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27931                 
27932                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27933                 
27934                 break;
27935             case 270 :
27936                 
27937                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27938                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27939                 
27940                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27941                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27942                 
27943                 var targetWidth = this.minWidth - 2 * x;
27944                 var targetHeight = this.minHeight - 2 * y;
27945                 
27946                 var scale = 1;
27947                 
27948                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27949                     scale = targetWidth / width;
27950                 }
27951                 
27952                 if(x > 0 && y == 0){
27953                     scale = targetHeight / height;
27954                 }
27955                 
27956                 if(x > 0 && y > 0){
27957                     scale = targetWidth / width;
27958                     
27959                     if(width < height){
27960                         scale = targetHeight / height;
27961                     }
27962                 }
27963                 
27964                 context.scale(scale, scale);
27965                 
27966                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27967                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27968
27969                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27970                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27971                 
27972                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27973                 
27974                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27975                 
27976                 break;
27977             default : 
27978                 break;
27979         }
27980         
27981         this.cropData = canvas.toDataURL(this.cropType);
27982         
27983         if(this.fireEvent('crop', this, this.cropData) !== false){
27984             this.process(this.file, this.cropData);
27985         }
27986         
27987         return;
27988         
27989     },
27990     
27991     setThumbBoxSize : function()
27992     {
27993         var width, height;
27994         
27995         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27996             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27997             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27998             
27999             this.minWidth = width;
28000             this.minHeight = height;
28001             
28002             if(this.rotate == 90 || this.rotate == 270){
28003                 this.minWidth = height;
28004                 this.minHeight = width;
28005             }
28006         }
28007         
28008         height = 300;
28009         width = Math.ceil(this.minWidth * height / this.minHeight);
28010         
28011         if(this.minWidth > this.minHeight){
28012             width = 300;
28013             height = Math.ceil(this.minHeight * width / this.minWidth);
28014         }
28015         
28016         this.thumbEl.setStyle({
28017             width : width + 'px',
28018             height : height + 'px'
28019         });
28020
28021         return;
28022             
28023     },
28024     
28025     setThumbBoxPosition : function()
28026     {
28027         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28028         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28029         
28030         this.thumbEl.setLeft(x);
28031         this.thumbEl.setTop(y);
28032         
28033     },
28034     
28035     baseRotateLevel : function()
28036     {
28037         this.baseRotate = 1;
28038         
28039         if(
28040                 typeof(this.exif) != 'undefined' &&
28041                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28042                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28043         ){
28044             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28045         }
28046         
28047         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28048         
28049     },
28050     
28051     baseScaleLevel : function()
28052     {
28053         var width, height;
28054         
28055         if(this.isDocument){
28056             
28057             if(this.baseRotate == 6 || this.baseRotate == 8){
28058             
28059                 height = this.thumbEl.getHeight();
28060                 this.baseScale = height / this.imageEl.OriginWidth;
28061
28062                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28063                     width = this.thumbEl.getWidth();
28064                     this.baseScale = width / this.imageEl.OriginHeight;
28065                 }
28066
28067                 return;
28068             }
28069
28070             height = this.thumbEl.getHeight();
28071             this.baseScale = height / this.imageEl.OriginHeight;
28072
28073             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28074                 width = this.thumbEl.getWidth();
28075                 this.baseScale = width / this.imageEl.OriginWidth;
28076             }
28077
28078             return;
28079         }
28080         
28081         if(this.baseRotate == 6 || this.baseRotate == 8){
28082             
28083             width = this.thumbEl.getHeight();
28084             this.baseScale = width / this.imageEl.OriginHeight;
28085             
28086             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28087                 height = this.thumbEl.getWidth();
28088                 this.baseScale = height / this.imageEl.OriginHeight;
28089             }
28090             
28091             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28092                 height = this.thumbEl.getWidth();
28093                 this.baseScale = height / this.imageEl.OriginHeight;
28094                 
28095                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28096                     width = this.thumbEl.getHeight();
28097                     this.baseScale = width / this.imageEl.OriginWidth;
28098                 }
28099             }
28100             
28101             return;
28102         }
28103         
28104         width = this.thumbEl.getWidth();
28105         this.baseScale = width / this.imageEl.OriginWidth;
28106         
28107         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28108             height = this.thumbEl.getHeight();
28109             this.baseScale = height / this.imageEl.OriginHeight;
28110         }
28111         
28112         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28113             
28114             height = this.thumbEl.getHeight();
28115             this.baseScale = height / this.imageEl.OriginHeight;
28116             
28117             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28118                 width = this.thumbEl.getWidth();
28119                 this.baseScale = width / this.imageEl.OriginWidth;
28120             }
28121             
28122         }
28123         
28124         return;
28125     },
28126     
28127     getScaleLevel : function()
28128     {
28129         return this.baseScale * Math.pow(1.1, this.scale);
28130     },
28131     
28132     onTouchStart : function(e)
28133     {
28134         if(!this.canvasLoaded){
28135             this.beforeSelectFile(e);
28136             return;
28137         }
28138         
28139         var touches = e.browserEvent.touches;
28140         
28141         if(!touches){
28142             return;
28143         }
28144         
28145         if(touches.length == 1){
28146             this.onMouseDown(e);
28147             return;
28148         }
28149         
28150         if(touches.length != 2){
28151             return;
28152         }
28153         
28154         var coords = [];
28155         
28156         for(var i = 0, finger; finger = touches[i]; i++){
28157             coords.push(finger.pageX, finger.pageY);
28158         }
28159         
28160         var x = Math.pow(coords[0] - coords[2], 2);
28161         var y = Math.pow(coords[1] - coords[3], 2);
28162         
28163         this.startDistance = Math.sqrt(x + y);
28164         
28165         this.startScale = this.scale;
28166         
28167         this.pinching = true;
28168         this.dragable = false;
28169         
28170     },
28171     
28172     onTouchMove : function(e)
28173     {
28174         if(!this.pinching && !this.dragable){
28175             return;
28176         }
28177         
28178         var touches = e.browserEvent.touches;
28179         
28180         if(!touches){
28181             return;
28182         }
28183         
28184         if(this.dragable){
28185             this.onMouseMove(e);
28186             return;
28187         }
28188         
28189         var coords = [];
28190         
28191         for(var i = 0, finger; finger = touches[i]; i++){
28192             coords.push(finger.pageX, finger.pageY);
28193         }
28194         
28195         var x = Math.pow(coords[0] - coords[2], 2);
28196         var y = Math.pow(coords[1] - coords[3], 2);
28197         
28198         this.endDistance = Math.sqrt(x + y);
28199         
28200         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28201         
28202         if(!this.zoomable()){
28203             this.scale = this.startScale;
28204             return;
28205         }
28206         
28207         this.draw();
28208         
28209     },
28210     
28211     onTouchEnd : function(e)
28212     {
28213         this.pinching = false;
28214         this.dragable = false;
28215         
28216     },
28217     
28218     process : function(file, crop)
28219     {
28220         if(this.loadMask){
28221             this.maskEl.mask(this.loadingText);
28222         }
28223         
28224         this.xhr = new XMLHttpRequest();
28225         
28226         file.xhr = this.xhr;
28227
28228         this.xhr.open(this.method, this.url, true);
28229         
28230         var headers = {
28231             "Accept": "application/json",
28232             "Cache-Control": "no-cache",
28233             "X-Requested-With": "XMLHttpRequest"
28234         };
28235         
28236         for (var headerName in headers) {
28237             var headerValue = headers[headerName];
28238             if (headerValue) {
28239                 this.xhr.setRequestHeader(headerName, headerValue);
28240             }
28241         }
28242         
28243         var _this = this;
28244         
28245         this.xhr.onload = function()
28246         {
28247             _this.xhrOnLoad(_this.xhr);
28248         }
28249         
28250         this.xhr.onerror = function()
28251         {
28252             _this.xhrOnError(_this.xhr);
28253         }
28254         
28255         var formData = new FormData();
28256
28257         formData.append('returnHTML', 'NO');
28258         
28259         if(crop){
28260             formData.append('crop', crop);
28261         }
28262         
28263         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28264             formData.append(this.paramName, file, file.name);
28265         }
28266         
28267         if(typeof(file.filename) != 'undefined'){
28268             formData.append('filename', file.filename);
28269         }
28270         
28271         if(typeof(file.mimetype) != 'undefined'){
28272             formData.append('mimetype', file.mimetype);
28273         }
28274         
28275         if(this.fireEvent('arrange', this, formData) != false){
28276             this.xhr.send(formData);
28277         };
28278     },
28279     
28280     xhrOnLoad : function(xhr)
28281     {
28282         if(this.loadMask){
28283             this.maskEl.unmask();
28284         }
28285         
28286         if (xhr.readyState !== 4) {
28287             this.fireEvent('exception', this, xhr);
28288             return;
28289         }
28290
28291         var response = Roo.decode(xhr.responseText);
28292         
28293         if(!response.success){
28294             this.fireEvent('exception', this, xhr);
28295             return;
28296         }
28297         
28298         var response = Roo.decode(xhr.responseText);
28299         
28300         this.fireEvent('upload', this, response);
28301         
28302     },
28303     
28304     xhrOnError : function()
28305     {
28306         if(this.loadMask){
28307             this.maskEl.unmask();
28308         }
28309         
28310         Roo.log('xhr on error');
28311         
28312         var response = Roo.decode(xhr.responseText);
28313           
28314         Roo.log(response);
28315         
28316     },
28317     
28318     prepare : function(file)
28319     {   
28320         if(this.loadMask){
28321             this.maskEl.mask(this.loadingText);
28322         }
28323         
28324         this.file = false;
28325         this.exif = {};
28326         
28327         if(typeof(file) === 'string'){
28328             this.loadCanvas(file);
28329             return;
28330         }
28331         
28332         if(!file || !this.urlAPI){
28333             return;
28334         }
28335         
28336         this.file = file;
28337         this.cropType = file.type;
28338         
28339         var _this = this;
28340         
28341         if(this.fireEvent('prepare', this, this.file) != false){
28342             
28343             var reader = new FileReader();
28344             
28345             reader.onload = function (e) {
28346                 if (e.target.error) {
28347                     Roo.log(e.target.error);
28348                     return;
28349                 }
28350                 
28351                 var buffer = e.target.result,
28352                     dataView = new DataView(buffer),
28353                     offset = 2,
28354                     maxOffset = dataView.byteLength - 4,
28355                     markerBytes,
28356                     markerLength;
28357                 
28358                 if (dataView.getUint16(0) === 0xffd8) {
28359                     while (offset < maxOffset) {
28360                         markerBytes = dataView.getUint16(offset);
28361                         
28362                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28363                             markerLength = dataView.getUint16(offset + 2) + 2;
28364                             if (offset + markerLength > dataView.byteLength) {
28365                                 Roo.log('Invalid meta data: Invalid segment size.');
28366                                 break;
28367                             }
28368                             
28369                             if(markerBytes == 0xffe1){
28370                                 _this.parseExifData(
28371                                     dataView,
28372                                     offset,
28373                                     markerLength
28374                                 );
28375                             }
28376                             
28377                             offset += markerLength;
28378                             
28379                             continue;
28380                         }
28381                         
28382                         break;
28383                     }
28384                     
28385                 }
28386                 
28387                 var url = _this.urlAPI.createObjectURL(_this.file);
28388                 
28389                 _this.loadCanvas(url);
28390                 
28391                 return;
28392             }
28393             
28394             reader.readAsArrayBuffer(this.file);
28395             
28396         }
28397         
28398     },
28399     
28400     parseExifData : function(dataView, offset, length)
28401     {
28402         var tiffOffset = offset + 10,
28403             littleEndian,
28404             dirOffset;
28405     
28406         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28407             // No Exif data, might be XMP data instead
28408             return;
28409         }
28410         
28411         // Check for the ASCII code for "Exif" (0x45786966):
28412         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28413             // No Exif data, might be XMP data instead
28414             return;
28415         }
28416         if (tiffOffset + 8 > dataView.byteLength) {
28417             Roo.log('Invalid Exif data: Invalid segment size.');
28418             return;
28419         }
28420         // Check for the two null bytes:
28421         if (dataView.getUint16(offset + 8) !== 0x0000) {
28422             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28423             return;
28424         }
28425         // Check the byte alignment:
28426         switch (dataView.getUint16(tiffOffset)) {
28427         case 0x4949:
28428             littleEndian = true;
28429             break;
28430         case 0x4D4D:
28431             littleEndian = false;
28432             break;
28433         default:
28434             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28435             return;
28436         }
28437         // Check for the TIFF tag marker (0x002A):
28438         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28439             Roo.log('Invalid Exif data: Missing TIFF marker.');
28440             return;
28441         }
28442         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28443         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28444         
28445         this.parseExifTags(
28446             dataView,
28447             tiffOffset,
28448             tiffOffset + dirOffset,
28449             littleEndian
28450         );
28451     },
28452     
28453     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28454     {
28455         var tagsNumber,
28456             dirEndOffset,
28457             i;
28458         if (dirOffset + 6 > dataView.byteLength) {
28459             Roo.log('Invalid Exif data: Invalid directory offset.');
28460             return;
28461         }
28462         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28463         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28464         if (dirEndOffset + 4 > dataView.byteLength) {
28465             Roo.log('Invalid Exif data: Invalid directory size.');
28466             return;
28467         }
28468         for (i = 0; i < tagsNumber; i += 1) {
28469             this.parseExifTag(
28470                 dataView,
28471                 tiffOffset,
28472                 dirOffset + 2 + 12 * i, // tag offset
28473                 littleEndian
28474             );
28475         }
28476         // Return the offset to the next directory:
28477         return dataView.getUint32(dirEndOffset, littleEndian);
28478     },
28479     
28480     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28481     {
28482         var tag = dataView.getUint16(offset, littleEndian);
28483         
28484         this.exif[tag] = this.getExifValue(
28485             dataView,
28486             tiffOffset,
28487             offset,
28488             dataView.getUint16(offset + 2, littleEndian), // tag type
28489             dataView.getUint32(offset + 4, littleEndian), // tag length
28490             littleEndian
28491         );
28492     },
28493     
28494     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28495     {
28496         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28497             tagSize,
28498             dataOffset,
28499             values,
28500             i,
28501             str,
28502             c;
28503     
28504         if (!tagType) {
28505             Roo.log('Invalid Exif data: Invalid tag type.');
28506             return;
28507         }
28508         
28509         tagSize = tagType.size * length;
28510         // Determine if the value is contained in the dataOffset bytes,
28511         // or if the value at the dataOffset is a pointer to the actual data:
28512         dataOffset = tagSize > 4 ?
28513                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28514         if (dataOffset + tagSize > dataView.byteLength) {
28515             Roo.log('Invalid Exif data: Invalid data offset.');
28516             return;
28517         }
28518         if (length === 1) {
28519             return tagType.getValue(dataView, dataOffset, littleEndian);
28520         }
28521         values = [];
28522         for (i = 0; i < length; i += 1) {
28523             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28524         }
28525         
28526         if (tagType.ascii) {
28527             str = '';
28528             // Concatenate the chars:
28529             for (i = 0; i < values.length; i += 1) {
28530                 c = values[i];
28531                 // Ignore the terminating NULL byte(s):
28532                 if (c === '\u0000') {
28533                     break;
28534                 }
28535                 str += c;
28536             }
28537             return str;
28538         }
28539         return values;
28540     }
28541     
28542 });
28543
28544 Roo.apply(Roo.bootstrap.UploadCropbox, {
28545     tags : {
28546         'Orientation': 0x0112
28547     },
28548     
28549     Orientation: {
28550             1: 0, //'top-left',
28551 //            2: 'top-right',
28552             3: 180, //'bottom-right',
28553 //            4: 'bottom-left',
28554 //            5: 'left-top',
28555             6: 90, //'right-top',
28556 //            7: 'right-bottom',
28557             8: 270 //'left-bottom'
28558     },
28559     
28560     exifTagTypes : {
28561         // byte, 8-bit unsigned int:
28562         1: {
28563             getValue: function (dataView, dataOffset) {
28564                 return dataView.getUint8(dataOffset);
28565             },
28566             size: 1
28567         },
28568         // ascii, 8-bit byte:
28569         2: {
28570             getValue: function (dataView, dataOffset) {
28571                 return String.fromCharCode(dataView.getUint8(dataOffset));
28572             },
28573             size: 1,
28574             ascii: true
28575         },
28576         // short, 16 bit int:
28577         3: {
28578             getValue: function (dataView, dataOffset, littleEndian) {
28579                 return dataView.getUint16(dataOffset, littleEndian);
28580             },
28581             size: 2
28582         },
28583         // long, 32 bit int:
28584         4: {
28585             getValue: function (dataView, dataOffset, littleEndian) {
28586                 return dataView.getUint32(dataOffset, littleEndian);
28587             },
28588             size: 4
28589         },
28590         // rational = two long values, first is numerator, second is denominator:
28591         5: {
28592             getValue: function (dataView, dataOffset, littleEndian) {
28593                 return dataView.getUint32(dataOffset, littleEndian) /
28594                     dataView.getUint32(dataOffset + 4, littleEndian);
28595             },
28596             size: 8
28597         },
28598         // slong, 32 bit signed int:
28599         9: {
28600             getValue: function (dataView, dataOffset, littleEndian) {
28601                 return dataView.getInt32(dataOffset, littleEndian);
28602             },
28603             size: 4
28604         },
28605         // srational, two slongs, first is numerator, second is denominator:
28606         10: {
28607             getValue: function (dataView, dataOffset, littleEndian) {
28608                 return dataView.getInt32(dataOffset, littleEndian) /
28609                     dataView.getInt32(dataOffset + 4, littleEndian);
28610             },
28611             size: 8
28612         }
28613     },
28614     
28615     footer : {
28616         STANDARD : [
28617             {
28618                 tag : 'div',
28619                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28620                 action : 'rotate-left',
28621                 cn : [
28622                     {
28623                         tag : 'button',
28624                         cls : 'btn btn-default',
28625                         html : '<i class="fa fa-undo"></i>'
28626                     }
28627                 ]
28628             },
28629             {
28630                 tag : 'div',
28631                 cls : 'btn-group roo-upload-cropbox-picture',
28632                 action : 'picture',
28633                 cn : [
28634                     {
28635                         tag : 'button',
28636                         cls : 'btn btn-default',
28637                         html : '<i class="fa fa-picture-o"></i>'
28638                     }
28639                 ]
28640             },
28641             {
28642                 tag : 'div',
28643                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28644                 action : 'rotate-right',
28645                 cn : [
28646                     {
28647                         tag : 'button',
28648                         cls : 'btn btn-default',
28649                         html : '<i class="fa fa-repeat"></i>'
28650                     }
28651                 ]
28652             }
28653         ],
28654         DOCUMENT : [
28655             {
28656                 tag : 'div',
28657                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28658                 action : 'rotate-left',
28659                 cn : [
28660                     {
28661                         tag : 'button',
28662                         cls : 'btn btn-default',
28663                         html : '<i class="fa fa-undo"></i>'
28664                     }
28665                 ]
28666             },
28667             {
28668                 tag : 'div',
28669                 cls : 'btn-group roo-upload-cropbox-download',
28670                 action : 'download',
28671                 cn : [
28672                     {
28673                         tag : 'button',
28674                         cls : 'btn btn-default',
28675                         html : '<i class="fa fa-download"></i>'
28676                     }
28677                 ]
28678             },
28679             {
28680                 tag : 'div',
28681                 cls : 'btn-group roo-upload-cropbox-crop',
28682                 action : 'crop',
28683                 cn : [
28684                     {
28685                         tag : 'button',
28686                         cls : 'btn btn-default',
28687                         html : '<i class="fa fa-crop"></i>'
28688                     }
28689                 ]
28690             },
28691             {
28692                 tag : 'div',
28693                 cls : 'btn-group roo-upload-cropbox-trash',
28694                 action : 'trash',
28695                 cn : [
28696                     {
28697                         tag : 'button',
28698                         cls : 'btn btn-default',
28699                         html : '<i class="fa fa-trash"></i>'
28700                     }
28701                 ]
28702             },
28703             {
28704                 tag : 'div',
28705                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28706                 action : 'rotate-right',
28707                 cn : [
28708                     {
28709                         tag : 'button',
28710                         cls : 'btn btn-default',
28711                         html : '<i class="fa fa-repeat"></i>'
28712                     }
28713                 ]
28714             }
28715         ],
28716         ROTATOR : [
28717             {
28718                 tag : 'div',
28719                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28720                 action : 'rotate-left',
28721                 cn : [
28722                     {
28723                         tag : 'button',
28724                         cls : 'btn btn-default',
28725                         html : '<i class="fa fa-undo"></i>'
28726                     }
28727                 ]
28728             },
28729             {
28730                 tag : 'div',
28731                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28732                 action : 'rotate-right',
28733                 cn : [
28734                     {
28735                         tag : 'button',
28736                         cls : 'btn btn-default',
28737                         html : '<i class="fa fa-repeat"></i>'
28738                     }
28739                 ]
28740             }
28741         ]
28742     }
28743 });
28744
28745 /*
28746 * Licence: LGPL
28747 */
28748
28749 /**
28750  * @class Roo.bootstrap.DocumentManager
28751  * @extends Roo.bootstrap.Component
28752  * Bootstrap DocumentManager class
28753  * @cfg {String} paramName default 'imageUpload'
28754  * @cfg {String} toolTipName default 'filename'
28755  * @cfg {String} method default POST
28756  * @cfg {String} url action url
28757  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28758  * @cfg {Boolean} multiple multiple upload default true
28759  * @cfg {Number} thumbSize default 300
28760  * @cfg {String} fieldLabel
28761  * @cfg {Number} labelWidth default 4
28762  * @cfg {String} labelAlign (left|top) default left
28763  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28764 * @cfg {Number} labellg set the width of label (1-12)
28765  * @cfg {Number} labelmd set the width of label (1-12)
28766  * @cfg {Number} labelsm set the width of label (1-12)
28767  * @cfg {Number} labelxs set the width of label (1-12)
28768  * 
28769  * @constructor
28770  * Create a new DocumentManager
28771  * @param {Object} config The config object
28772  */
28773
28774 Roo.bootstrap.DocumentManager = function(config){
28775     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28776     
28777     this.files = [];
28778     this.delegates = [];
28779     
28780     this.addEvents({
28781         /**
28782          * @event initial
28783          * Fire when initial the DocumentManager
28784          * @param {Roo.bootstrap.DocumentManager} this
28785          */
28786         "initial" : true,
28787         /**
28788          * @event inspect
28789          * inspect selected file
28790          * @param {Roo.bootstrap.DocumentManager} this
28791          * @param {File} file
28792          */
28793         "inspect" : true,
28794         /**
28795          * @event exception
28796          * Fire when xhr load exception
28797          * @param {Roo.bootstrap.DocumentManager} this
28798          * @param {XMLHttpRequest} xhr
28799          */
28800         "exception" : true,
28801         /**
28802          * @event afterupload
28803          * Fire when xhr load exception
28804          * @param {Roo.bootstrap.DocumentManager} this
28805          * @param {XMLHttpRequest} xhr
28806          */
28807         "afterupload" : true,
28808         /**
28809          * @event prepare
28810          * prepare the form data
28811          * @param {Roo.bootstrap.DocumentManager} this
28812          * @param {Object} formData
28813          */
28814         "prepare" : true,
28815         /**
28816          * @event remove
28817          * Fire when remove the file
28818          * @param {Roo.bootstrap.DocumentManager} this
28819          * @param {Object} file
28820          */
28821         "remove" : true,
28822         /**
28823          * @event refresh
28824          * Fire after refresh the file
28825          * @param {Roo.bootstrap.DocumentManager} this
28826          */
28827         "refresh" : true,
28828         /**
28829          * @event click
28830          * Fire after click the image
28831          * @param {Roo.bootstrap.DocumentManager} this
28832          * @param {Object} file
28833          */
28834         "click" : true,
28835         /**
28836          * @event edit
28837          * Fire when upload a image and editable set to true
28838          * @param {Roo.bootstrap.DocumentManager} this
28839          * @param {Object} file
28840          */
28841         "edit" : true,
28842         /**
28843          * @event beforeselectfile
28844          * Fire before select file
28845          * @param {Roo.bootstrap.DocumentManager} this
28846          */
28847         "beforeselectfile" : true,
28848         /**
28849          * @event process
28850          * Fire before process file
28851          * @param {Roo.bootstrap.DocumentManager} this
28852          * @param {Object} file
28853          */
28854         "process" : true,
28855         /**
28856          * @event previewrendered
28857          * Fire when preview rendered
28858          * @param {Roo.bootstrap.DocumentManager} this
28859          * @param {Object} file
28860          */
28861         "previewrendered" : true,
28862         /**
28863          */
28864         "previewResize" : true
28865         
28866     });
28867 };
28868
28869 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28870     
28871     boxes : 0,
28872     inputName : '',
28873     thumbSize : 300,
28874     multiple : true,
28875     files : false,
28876     method : 'POST',
28877     url : '',
28878     paramName : 'imageUpload',
28879     toolTipName : 'filename',
28880     fieldLabel : '',
28881     labelWidth : 4,
28882     labelAlign : 'left',
28883     editable : true,
28884     delegates : false,
28885     xhr : false, 
28886     
28887     labellg : 0,
28888     labelmd : 0,
28889     labelsm : 0,
28890     labelxs : 0,
28891     
28892     getAutoCreate : function()
28893     {   
28894         var managerWidget = {
28895             tag : 'div',
28896             cls : 'roo-document-manager',
28897             cn : [
28898                 {
28899                     tag : 'input',
28900                     cls : 'roo-document-manager-selector',
28901                     type : 'file'
28902                 },
28903                 {
28904                     tag : 'div',
28905                     cls : 'roo-document-manager-uploader',
28906                     cn : [
28907                         {
28908                             tag : 'div',
28909                             cls : 'roo-document-manager-upload-btn',
28910                             html : '<i class="fa fa-plus"></i>'
28911                         }
28912                     ]
28913                     
28914                 }
28915             ]
28916         };
28917         
28918         var content = [
28919             {
28920                 tag : 'div',
28921                 cls : 'column col-md-12',
28922                 cn : managerWidget
28923             }
28924         ];
28925         
28926         if(this.fieldLabel.length){
28927             
28928             content = [
28929                 {
28930                     tag : 'div',
28931                     cls : 'column col-md-12',
28932                     html : this.fieldLabel
28933                 },
28934                 {
28935                     tag : 'div',
28936                     cls : 'column col-md-12',
28937                     cn : managerWidget
28938                 }
28939             ];
28940
28941             if(this.labelAlign == 'left'){
28942                 content = [
28943                     {
28944                         tag : 'div',
28945                         cls : 'column',
28946                         html : this.fieldLabel
28947                     },
28948                     {
28949                         tag : 'div',
28950                         cls : 'column',
28951                         cn : managerWidget
28952                     }
28953                 ];
28954                 
28955                 if(this.labelWidth > 12){
28956                     content[0].style = "width: " + this.labelWidth + 'px';
28957                 }
28958
28959                 if(this.labelWidth < 13 && this.labelmd == 0){
28960                     this.labelmd = this.labelWidth;
28961                 }
28962
28963                 if(this.labellg > 0){
28964                     content[0].cls += ' col-lg-' + this.labellg;
28965                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28966                 }
28967
28968                 if(this.labelmd > 0){
28969                     content[0].cls += ' col-md-' + this.labelmd;
28970                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28971                 }
28972
28973                 if(this.labelsm > 0){
28974                     content[0].cls += ' col-sm-' + this.labelsm;
28975                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28976                 }
28977
28978                 if(this.labelxs > 0){
28979                     content[0].cls += ' col-xs-' + this.labelxs;
28980                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28981                 }
28982                 
28983             }
28984         }
28985         
28986         var cfg = {
28987             tag : 'div',
28988             cls : 'row clearfix',
28989             cn : content
28990         };
28991         
28992         return cfg;
28993         
28994     },
28995     
28996     initEvents : function()
28997     {
28998         this.managerEl = this.el.select('.roo-document-manager', true).first();
28999         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29000         
29001         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29002         this.selectorEl.hide();
29003         
29004         if(this.multiple){
29005             this.selectorEl.attr('multiple', 'multiple');
29006         }
29007         
29008         this.selectorEl.on('change', this.onFileSelected, this);
29009         
29010         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29011         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29012         
29013         this.uploader.on('click', this.onUploaderClick, this);
29014         
29015         this.renderProgressDialog();
29016         
29017         var _this = this;
29018         
29019         window.addEventListener("resize", function() { _this.refresh(); } );
29020         
29021         this.fireEvent('initial', this);
29022     },
29023     
29024     renderProgressDialog : function()
29025     {
29026         var _this = this;
29027         
29028         this.progressDialog = new Roo.bootstrap.Modal({
29029             cls : 'roo-document-manager-progress-dialog',
29030             allow_close : false,
29031             title : '',
29032             buttons : [
29033                 {
29034                     name  :'cancel',
29035                     weight : 'danger',
29036                     html : 'Cancel'
29037                 }
29038             ], 
29039             listeners : { 
29040                 btnclick : function() {
29041                     _this.uploadCancel();
29042                     this.hide();
29043                 }
29044             }
29045         });
29046          
29047         this.progressDialog.render(Roo.get(document.body));
29048          
29049         this.progress = new Roo.bootstrap.Progress({
29050             cls : 'roo-document-manager-progress',
29051             active : true,
29052             striped : true
29053         });
29054         
29055         this.progress.render(this.progressDialog.getChildContainer());
29056         
29057         this.progressBar = new Roo.bootstrap.ProgressBar({
29058             cls : 'roo-document-manager-progress-bar',
29059             aria_valuenow : 0,
29060             aria_valuemin : 0,
29061             aria_valuemax : 12,
29062             panel : 'success'
29063         });
29064         
29065         this.progressBar.render(this.progress.getChildContainer());
29066     },
29067     
29068     onUploaderClick : function(e)
29069     {
29070         e.preventDefault();
29071      
29072         if(this.fireEvent('beforeselectfile', this) != false){
29073             this.selectorEl.dom.click();
29074         }
29075         
29076     },
29077     
29078     onFileSelected : function(e)
29079     {
29080         e.preventDefault();
29081         
29082         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29083             return;
29084         }
29085         
29086         Roo.each(this.selectorEl.dom.files, function(file){
29087             if(this.fireEvent('inspect', this, file) != false){
29088                 this.files.push(file);
29089             }
29090         }, this);
29091         
29092         this.queue();
29093         
29094     },
29095     
29096     queue : function()
29097     {
29098         this.selectorEl.dom.value = '';
29099         
29100         if(!this.files || !this.files.length){
29101             return;
29102         }
29103         
29104         if(this.boxes > 0 && this.files.length > this.boxes){
29105             this.files = this.files.slice(0, this.boxes);
29106         }
29107         
29108         this.uploader.show();
29109         
29110         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29111             this.uploader.hide();
29112         }
29113         
29114         var _this = this;
29115         
29116         var files = [];
29117         
29118         var docs = [];
29119         
29120         Roo.each(this.files, function(file){
29121             
29122             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29123                 var f = this.renderPreview(file);
29124                 files.push(f);
29125                 return;
29126             }
29127             
29128             if(file.type.indexOf('image') != -1){
29129                 this.delegates.push(
29130                     (function(){
29131                         _this.process(file);
29132                     }).createDelegate(this)
29133                 );
29134         
29135                 return;
29136             }
29137             
29138             docs.push(
29139                 (function(){
29140                     _this.process(file);
29141                 }).createDelegate(this)
29142             );
29143             
29144         }, this);
29145         
29146         this.files = files;
29147         
29148         this.delegates = this.delegates.concat(docs);
29149         
29150         if(!this.delegates.length){
29151             this.refresh();
29152             return;
29153         }
29154         
29155         this.progressBar.aria_valuemax = this.delegates.length;
29156         
29157         this.arrange();
29158         
29159         return;
29160     },
29161     
29162     arrange : function()
29163     {
29164         if(!this.delegates.length){
29165             this.progressDialog.hide();
29166             this.refresh();
29167             return;
29168         }
29169         
29170         var delegate = this.delegates.shift();
29171         
29172         this.progressDialog.show();
29173         
29174         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29175         
29176         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29177         
29178         delegate();
29179     },
29180     
29181     refresh : function()
29182     {
29183         this.uploader.show();
29184         
29185         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29186             this.uploader.hide();
29187         }
29188         
29189         Roo.isTouch ? this.closable(false) : this.closable(true);
29190         
29191         this.fireEvent('refresh', this);
29192     },
29193     
29194     onRemove : function(e, el, o)
29195     {
29196         e.preventDefault();
29197         
29198         this.fireEvent('remove', this, o);
29199         
29200     },
29201     
29202     remove : function(o)
29203     {
29204         var files = [];
29205         
29206         Roo.each(this.files, function(file){
29207             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29208                 files.push(file);
29209                 return;
29210             }
29211
29212             o.target.remove();
29213
29214         }, this);
29215         
29216         this.files = files;
29217         
29218         this.refresh();
29219     },
29220     
29221     clear : function()
29222     {
29223         Roo.each(this.files, function(file){
29224             if(!file.target){
29225                 return;
29226             }
29227             
29228             file.target.remove();
29229
29230         }, this);
29231         
29232         this.files = [];
29233         
29234         this.refresh();
29235     },
29236     
29237     onClick : function(e, el, o)
29238     {
29239         e.preventDefault();
29240         
29241         this.fireEvent('click', this, o);
29242         
29243     },
29244     
29245     closable : function(closable)
29246     {
29247         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29248             
29249             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29250             
29251             if(closable){
29252                 el.show();
29253                 return;
29254             }
29255             
29256             el.hide();
29257             
29258         }, this);
29259     },
29260     
29261     xhrOnLoad : function(xhr)
29262     {
29263         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29264             el.remove();
29265         }, this);
29266         
29267         if (xhr.readyState !== 4) {
29268             this.arrange();
29269             this.fireEvent('exception', this, xhr);
29270             return;
29271         }
29272
29273         var response = Roo.decode(xhr.responseText);
29274         
29275         if(!response.success){
29276             this.arrange();
29277             this.fireEvent('exception', this, xhr);
29278             return;
29279         }
29280         
29281         var file = this.renderPreview(response.data);
29282         
29283         this.files.push(file);
29284         
29285         this.arrange();
29286         
29287         this.fireEvent('afterupload', this, xhr);
29288         
29289     },
29290     
29291     xhrOnError : function(xhr)
29292     {
29293         Roo.log('xhr on error');
29294         
29295         var response = Roo.decode(xhr.responseText);
29296           
29297         Roo.log(response);
29298         
29299         this.arrange();
29300     },
29301     
29302     process : function(file)
29303     {
29304         if(this.fireEvent('process', this, file) !== false){
29305             if(this.editable && file.type.indexOf('image') != -1){
29306                 this.fireEvent('edit', this, file);
29307                 return;
29308             }
29309
29310             this.uploadStart(file, false);
29311
29312             return;
29313         }
29314         
29315     },
29316     
29317     uploadStart : function(file, crop)
29318     {
29319         this.xhr = new XMLHttpRequest();
29320         
29321         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29322             this.arrange();
29323             return;
29324         }
29325         
29326         file.xhr = this.xhr;
29327             
29328         this.managerEl.createChild({
29329             tag : 'div',
29330             cls : 'roo-document-manager-loading',
29331             cn : [
29332                 {
29333                     tag : 'div',
29334                     tooltip : file.name,
29335                     cls : 'roo-document-manager-thumb',
29336                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29337                 }
29338             ]
29339
29340         });
29341
29342         this.xhr.open(this.method, this.url, true);
29343         
29344         var headers = {
29345             "Accept": "application/json",
29346             "Cache-Control": "no-cache",
29347             "X-Requested-With": "XMLHttpRequest"
29348         };
29349         
29350         for (var headerName in headers) {
29351             var headerValue = headers[headerName];
29352             if (headerValue) {
29353                 this.xhr.setRequestHeader(headerName, headerValue);
29354             }
29355         }
29356         
29357         var _this = this;
29358         
29359         this.xhr.onload = function()
29360         {
29361             _this.xhrOnLoad(_this.xhr);
29362         }
29363         
29364         this.xhr.onerror = function()
29365         {
29366             _this.xhrOnError(_this.xhr);
29367         }
29368         
29369         var formData = new FormData();
29370
29371         formData.append('returnHTML', 'NO');
29372         
29373         if(crop){
29374             formData.append('crop', crop);
29375         }
29376         
29377         formData.append(this.paramName, file, file.name);
29378         
29379         var options = {
29380             file : file, 
29381             manually : false
29382         };
29383         
29384         if(this.fireEvent('prepare', this, formData, options) != false){
29385             
29386             if(options.manually){
29387                 return;
29388             }
29389             
29390             this.xhr.send(formData);
29391             return;
29392         };
29393         
29394         this.uploadCancel();
29395     },
29396     
29397     uploadCancel : function()
29398     {
29399         if (this.xhr) {
29400             this.xhr.abort();
29401         }
29402         
29403         this.delegates = [];
29404         
29405         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29406             el.remove();
29407         }, this);
29408         
29409         this.arrange();
29410     },
29411     
29412     renderPreview : function(file)
29413     {
29414         if(typeof(file.target) != 'undefined' && file.target){
29415             return file;
29416         }
29417         
29418         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29419         
29420         var previewEl = this.managerEl.createChild({
29421             tag : 'div',
29422             cls : 'roo-document-manager-preview',
29423             cn : [
29424                 {
29425                     tag : 'div',
29426                     tooltip : file[this.toolTipName],
29427                     cls : 'roo-document-manager-thumb',
29428                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29429                 },
29430                 {
29431                     tag : 'button',
29432                     cls : 'close',
29433                     html : '<i class="fa fa-times-circle"></i>'
29434                 }
29435             ]
29436         });
29437
29438         var close = previewEl.select('button.close', true).first();
29439
29440         close.on('click', this.onRemove, this, file);
29441
29442         file.target = previewEl;
29443
29444         var image = previewEl.select('img', true).first();
29445         
29446         var _this = this;
29447         
29448         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29449         
29450         image.on('click', this.onClick, this, file);
29451         
29452         this.fireEvent('previewrendered', this, file);
29453         
29454         return file;
29455         
29456     },
29457     
29458     onPreviewLoad : function(file, image)
29459     {
29460         if(typeof(file.target) == 'undefined' || !file.target){
29461             return;
29462         }
29463         
29464         var width = image.dom.naturalWidth || image.dom.width;
29465         var height = image.dom.naturalHeight || image.dom.height;
29466         
29467         if(!this.previewResize) {
29468             return;
29469         }
29470         
29471         if(width > height){
29472             file.target.addClass('wide');
29473             return;
29474         }
29475         
29476         file.target.addClass('tall');
29477         return;
29478         
29479     },
29480     
29481     uploadFromSource : function(file, crop)
29482     {
29483         this.xhr = new XMLHttpRequest();
29484         
29485         this.managerEl.createChild({
29486             tag : 'div',
29487             cls : 'roo-document-manager-loading',
29488             cn : [
29489                 {
29490                     tag : 'div',
29491                     tooltip : file.name,
29492                     cls : 'roo-document-manager-thumb',
29493                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29494                 }
29495             ]
29496
29497         });
29498
29499         this.xhr.open(this.method, this.url, true);
29500         
29501         var headers = {
29502             "Accept": "application/json",
29503             "Cache-Control": "no-cache",
29504             "X-Requested-With": "XMLHttpRequest"
29505         };
29506         
29507         for (var headerName in headers) {
29508             var headerValue = headers[headerName];
29509             if (headerValue) {
29510                 this.xhr.setRequestHeader(headerName, headerValue);
29511             }
29512         }
29513         
29514         var _this = this;
29515         
29516         this.xhr.onload = function()
29517         {
29518             _this.xhrOnLoad(_this.xhr);
29519         }
29520         
29521         this.xhr.onerror = function()
29522         {
29523             _this.xhrOnError(_this.xhr);
29524         }
29525         
29526         var formData = new FormData();
29527
29528         formData.append('returnHTML', 'NO');
29529         
29530         formData.append('crop', crop);
29531         
29532         if(typeof(file.filename) != 'undefined'){
29533             formData.append('filename', file.filename);
29534         }
29535         
29536         if(typeof(file.mimetype) != 'undefined'){
29537             formData.append('mimetype', file.mimetype);
29538         }
29539         
29540         Roo.log(formData);
29541         
29542         if(this.fireEvent('prepare', this, formData) != false){
29543             this.xhr.send(formData);
29544         };
29545     }
29546 });
29547
29548 /*
29549 * Licence: LGPL
29550 */
29551
29552 /**
29553  * @class Roo.bootstrap.DocumentViewer
29554  * @extends Roo.bootstrap.Component
29555  * Bootstrap DocumentViewer class
29556  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29557  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29558  * 
29559  * @constructor
29560  * Create a new DocumentViewer
29561  * @param {Object} config The config object
29562  */
29563
29564 Roo.bootstrap.DocumentViewer = function(config){
29565     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29566     
29567     this.addEvents({
29568         /**
29569          * @event initial
29570          * Fire after initEvent
29571          * @param {Roo.bootstrap.DocumentViewer} this
29572          */
29573         "initial" : true,
29574         /**
29575          * @event click
29576          * Fire after click
29577          * @param {Roo.bootstrap.DocumentViewer} this
29578          */
29579         "click" : true,
29580         /**
29581          * @event download
29582          * Fire after download button
29583          * @param {Roo.bootstrap.DocumentViewer} this
29584          */
29585         "download" : true,
29586         /**
29587          * @event trash
29588          * Fire after trash button
29589          * @param {Roo.bootstrap.DocumentViewer} this
29590          */
29591         "trash" : true
29592         
29593     });
29594 };
29595
29596 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29597     
29598     showDownload : true,
29599     
29600     showTrash : true,
29601     
29602     getAutoCreate : function()
29603     {
29604         var cfg = {
29605             tag : 'div',
29606             cls : 'roo-document-viewer',
29607             cn : [
29608                 {
29609                     tag : 'div',
29610                     cls : 'roo-document-viewer-body',
29611                     cn : [
29612                         {
29613                             tag : 'div',
29614                             cls : 'roo-document-viewer-thumb',
29615                             cn : [
29616                                 {
29617                                     tag : 'img',
29618                                     cls : 'roo-document-viewer-image'
29619                                 }
29620                             ]
29621                         }
29622                     ]
29623                 },
29624                 {
29625                     tag : 'div',
29626                     cls : 'roo-document-viewer-footer',
29627                     cn : {
29628                         tag : 'div',
29629                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29630                         cn : [
29631                             {
29632                                 tag : 'div',
29633                                 cls : 'btn-group roo-document-viewer-download',
29634                                 cn : [
29635                                     {
29636                                         tag : 'button',
29637                                         cls : 'btn btn-default',
29638                                         html : '<i class="fa fa-download"></i>'
29639                                     }
29640                                 ]
29641                             },
29642                             {
29643                                 tag : 'div',
29644                                 cls : 'btn-group roo-document-viewer-trash',
29645                                 cn : [
29646                                     {
29647                                         tag : 'button',
29648                                         cls : 'btn btn-default',
29649                                         html : '<i class="fa fa-trash"></i>'
29650                                     }
29651                                 ]
29652                             }
29653                         ]
29654                     }
29655                 }
29656             ]
29657         };
29658         
29659         return cfg;
29660     },
29661     
29662     initEvents : function()
29663     {
29664         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29665         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29666         
29667         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29668         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29669         
29670         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29671         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29672         
29673         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29674         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29675         
29676         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29677         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29678         
29679         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29680         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29681         
29682         this.bodyEl.on('click', this.onClick, this);
29683         this.downloadBtn.on('click', this.onDownload, this);
29684         this.trashBtn.on('click', this.onTrash, this);
29685         
29686         this.downloadBtn.hide();
29687         this.trashBtn.hide();
29688         
29689         if(this.showDownload){
29690             this.downloadBtn.show();
29691         }
29692         
29693         if(this.showTrash){
29694             this.trashBtn.show();
29695         }
29696         
29697         if(!this.showDownload && !this.showTrash) {
29698             this.footerEl.hide();
29699         }
29700         
29701     },
29702     
29703     initial : function()
29704     {
29705         this.fireEvent('initial', this);
29706         
29707     },
29708     
29709     onClick : function(e)
29710     {
29711         e.preventDefault();
29712         
29713         this.fireEvent('click', this);
29714     },
29715     
29716     onDownload : function(e)
29717     {
29718         e.preventDefault();
29719         
29720         this.fireEvent('download', this);
29721     },
29722     
29723     onTrash : function(e)
29724     {
29725         e.preventDefault();
29726         
29727         this.fireEvent('trash', this);
29728     }
29729     
29730 });
29731 /*
29732  * - LGPL
29733  *
29734  * nav progress bar
29735  * 
29736  */
29737
29738 /**
29739  * @class Roo.bootstrap.NavProgressBar
29740  * @extends Roo.bootstrap.Component
29741  * Bootstrap NavProgressBar class
29742  * 
29743  * @constructor
29744  * Create a new nav progress bar
29745  * @param {Object} config The config object
29746  */
29747
29748 Roo.bootstrap.NavProgressBar = function(config){
29749     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29750
29751     this.bullets = this.bullets || [];
29752    
29753 //    Roo.bootstrap.NavProgressBar.register(this);
29754      this.addEvents({
29755         /**
29756              * @event changed
29757              * Fires when the active item changes
29758              * @param {Roo.bootstrap.NavProgressBar} this
29759              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29760              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29761          */
29762         'changed': true
29763      });
29764     
29765 };
29766
29767 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29768     
29769     bullets : [],
29770     barItems : [],
29771     
29772     getAutoCreate : function()
29773     {
29774         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29775         
29776         cfg = {
29777             tag : 'div',
29778             cls : 'roo-navigation-bar-group',
29779             cn : [
29780                 {
29781                     tag : 'div',
29782                     cls : 'roo-navigation-top-bar'
29783                 },
29784                 {
29785                     tag : 'div',
29786                     cls : 'roo-navigation-bullets-bar',
29787                     cn : [
29788                         {
29789                             tag : 'ul',
29790                             cls : 'roo-navigation-bar'
29791                         }
29792                     ]
29793                 },
29794                 
29795                 {
29796                     tag : 'div',
29797                     cls : 'roo-navigation-bottom-bar'
29798                 }
29799             ]
29800             
29801         };
29802         
29803         return cfg;
29804         
29805     },
29806     
29807     initEvents: function() 
29808     {
29809         
29810     },
29811     
29812     onRender : function(ct, position) 
29813     {
29814         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29815         
29816         if(this.bullets.length){
29817             Roo.each(this.bullets, function(b){
29818                this.addItem(b);
29819             }, this);
29820         }
29821         
29822         this.format();
29823         
29824     },
29825     
29826     addItem : function(cfg)
29827     {
29828         var item = new Roo.bootstrap.NavProgressItem(cfg);
29829         
29830         item.parentId = this.id;
29831         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29832         
29833         if(cfg.html){
29834             var top = new Roo.bootstrap.Element({
29835                 tag : 'div',
29836                 cls : 'roo-navigation-bar-text'
29837             });
29838             
29839             var bottom = new Roo.bootstrap.Element({
29840                 tag : 'div',
29841                 cls : 'roo-navigation-bar-text'
29842             });
29843             
29844             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29845             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29846             
29847             var topText = new Roo.bootstrap.Element({
29848                 tag : 'span',
29849                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29850             });
29851             
29852             var bottomText = new Roo.bootstrap.Element({
29853                 tag : 'span',
29854                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29855             });
29856             
29857             topText.onRender(top.el, null);
29858             bottomText.onRender(bottom.el, null);
29859             
29860             item.topEl = top;
29861             item.bottomEl = bottom;
29862         }
29863         
29864         this.barItems.push(item);
29865         
29866         return item;
29867     },
29868     
29869     getActive : function()
29870     {
29871         var active = false;
29872         
29873         Roo.each(this.barItems, function(v){
29874             
29875             if (!v.isActive()) {
29876                 return;
29877             }
29878             
29879             active = v;
29880             return false;
29881             
29882         });
29883         
29884         return active;
29885     },
29886     
29887     setActiveItem : function(item)
29888     {
29889         var prev = false;
29890         
29891         Roo.each(this.barItems, function(v){
29892             if (v.rid == item.rid) {
29893                 return ;
29894             }
29895             
29896             if (v.isActive()) {
29897                 v.setActive(false);
29898                 prev = v;
29899             }
29900         });
29901
29902         item.setActive(true);
29903         
29904         this.fireEvent('changed', this, item, prev);
29905     },
29906     
29907     getBarItem: function(rid)
29908     {
29909         var ret = false;
29910         
29911         Roo.each(this.barItems, function(e) {
29912             if (e.rid != rid) {
29913                 return;
29914             }
29915             
29916             ret =  e;
29917             return false;
29918         });
29919         
29920         return ret;
29921     },
29922     
29923     indexOfItem : function(item)
29924     {
29925         var index = false;
29926         
29927         Roo.each(this.barItems, function(v, i){
29928             
29929             if (v.rid != item.rid) {
29930                 return;
29931             }
29932             
29933             index = i;
29934             return false
29935         });
29936         
29937         return index;
29938     },
29939     
29940     setActiveNext : function()
29941     {
29942         var i = this.indexOfItem(this.getActive());
29943         
29944         if (i > this.barItems.length) {
29945             return;
29946         }
29947         
29948         this.setActiveItem(this.barItems[i+1]);
29949     },
29950     
29951     setActivePrev : function()
29952     {
29953         var i = this.indexOfItem(this.getActive());
29954         
29955         if (i  < 1) {
29956             return;
29957         }
29958         
29959         this.setActiveItem(this.barItems[i-1]);
29960     },
29961     
29962     format : function()
29963     {
29964         if(!this.barItems.length){
29965             return;
29966         }
29967      
29968         var width = 100 / this.barItems.length;
29969         
29970         Roo.each(this.barItems, function(i){
29971             i.el.setStyle('width', width + '%');
29972             i.topEl.el.setStyle('width', width + '%');
29973             i.bottomEl.el.setStyle('width', width + '%');
29974         }, this);
29975         
29976     }
29977     
29978 });
29979 /*
29980  * - LGPL
29981  *
29982  * Nav Progress Item
29983  * 
29984  */
29985
29986 /**
29987  * @class Roo.bootstrap.NavProgressItem
29988  * @extends Roo.bootstrap.Component
29989  * Bootstrap NavProgressItem class
29990  * @cfg {String} rid the reference id
29991  * @cfg {Boolean} active (true|false) Is item active default false
29992  * @cfg {Boolean} disabled (true|false) Is item active default false
29993  * @cfg {String} html
29994  * @cfg {String} position (top|bottom) text position default bottom
29995  * @cfg {String} icon show icon instead of number
29996  * 
29997  * @constructor
29998  * Create a new NavProgressItem
29999  * @param {Object} config The config object
30000  */
30001 Roo.bootstrap.NavProgressItem = function(config){
30002     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30003     this.addEvents({
30004         // raw events
30005         /**
30006          * @event click
30007          * The raw click event for the entire grid.
30008          * @param {Roo.bootstrap.NavProgressItem} this
30009          * @param {Roo.EventObject} e
30010          */
30011         "click" : true
30012     });
30013    
30014 };
30015
30016 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30017     
30018     rid : '',
30019     active : false,
30020     disabled : false,
30021     html : '',
30022     position : 'bottom',
30023     icon : false,
30024     
30025     getAutoCreate : function()
30026     {
30027         var iconCls = 'roo-navigation-bar-item-icon';
30028         
30029         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30030         
30031         var cfg = {
30032             tag: 'li',
30033             cls: 'roo-navigation-bar-item',
30034             cn : [
30035                 {
30036                     tag : 'i',
30037                     cls : iconCls
30038                 }
30039             ]
30040         };
30041         
30042         if(this.active){
30043             cfg.cls += ' active';
30044         }
30045         if(this.disabled){
30046             cfg.cls += ' disabled';
30047         }
30048         
30049         return cfg;
30050     },
30051     
30052     disable : function()
30053     {
30054         this.setDisabled(true);
30055     },
30056     
30057     enable : function()
30058     {
30059         this.setDisabled(false);
30060     },
30061     
30062     initEvents: function() 
30063     {
30064         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30065         
30066         this.iconEl.on('click', this.onClick, this);
30067     },
30068     
30069     onClick : function(e)
30070     {
30071         e.preventDefault();
30072         
30073         if(this.disabled){
30074             return;
30075         }
30076         
30077         if(this.fireEvent('click', this, e) === false){
30078             return;
30079         };
30080         
30081         this.parent().setActiveItem(this);
30082     },
30083     
30084     isActive: function () 
30085     {
30086         return this.active;
30087     },
30088     
30089     setActive : function(state)
30090     {
30091         if(this.active == state){
30092             return;
30093         }
30094         
30095         this.active = state;
30096         
30097         if (state) {
30098             this.el.addClass('active');
30099             return;
30100         }
30101         
30102         this.el.removeClass('active');
30103         
30104         return;
30105     },
30106     
30107     setDisabled : function(state)
30108     {
30109         if(this.disabled == state){
30110             return;
30111         }
30112         
30113         this.disabled = state;
30114         
30115         if (state) {
30116             this.el.addClass('disabled');
30117             return;
30118         }
30119         
30120         this.el.removeClass('disabled');
30121     },
30122     
30123     tooltipEl : function()
30124     {
30125         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30126     }
30127 });
30128  
30129
30130  /*
30131  * - LGPL
30132  *
30133  * FieldLabel
30134  * 
30135  */
30136
30137 /**
30138  * @class Roo.bootstrap.FieldLabel
30139  * @extends Roo.bootstrap.Component
30140  * Bootstrap FieldLabel class
30141  * @cfg {String} html contents of the element
30142  * @cfg {String} tag tag of the element default label
30143  * @cfg {String} cls class of the element
30144  * @cfg {String} target label target 
30145  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30146  * @cfg {String} invalidClass default "text-warning"
30147  * @cfg {String} validClass default "text-success"
30148  * @cfg {String} iconTooltip default "This field is required"
30149  * @cfg {String} indicatorpos (left|right) default left
30150  * 
30151  * @constructor
30152  * Create a new FieldLabel
30153  * @param {Object} config The config object
30154  */
30155
30156 Roo.bootstrap.FieldLabel = function(config){
30157     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30158     
30159     this.addEvents({
30160             /**
30161              * @event invalid
30162              * Fires after the field has been marked as invalid.
30163              * @param {Roo.form.FieldLabel} this
30164              * @param {String} msg The validation message
30165              */
30166             invalid : true,
30167             /**
30168              * @event valid
30169              * Fires after the field has been validated with no errors.
30170              * @param {Roo.form.FieldLabel} this
30171              */
30172             valid : true
30173         });
30174 };
30175
30176 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30177     
30178     tag: 'label',
30179     cls: '',
30180     html: '',
30181     target: '',
30182     allowBlank : true,
30183     invalidClass : 'has-warning',
30184     validClass : 'has-success',
30185     iconTooltip : 'This field is required',
30186     indicatorpos : 'left',
30187     
30188     getAutoCreate : function(){
30189         
30190         var cls = "";
30191         if (!this.allowBlank) {
30192             cls  = "visible";
30193         }
30194         
30195         var cfg = {
30196             tag : this.tag,
30197             cls : 'roo-bootstrap-field-label ' + this.cls,
30198             for : this.target,
30199             cn : [
30200                 {
30201                     tag : 'i',
30202                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30203                     tooltip : this.iconTooltip
30204                 },
30205                 {
30206                     tag : 'span',
30207                     html : this.html
30208                 }
30209             ] 
30210         };
30211         
30212         if(this.indicatorpos == 'right'){
30213             var cfg = {
30214                 tag : this.tag,
30215                 cls : 'roo-bootstrap-field-label ' + this.cls,
30216                 for : this.target,
30217                 cn : [
30218                     {
30219                         tag : 'span',
30220                         html : this.html
30221                     },
30222                     {
30223                         tag : 'i',
30224                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30225                         tooltip : this.iconTooltip
30226                     }
30227                 ] 
30228             };
30229         }
30230         
30231         return cfg;
30232     },
30233     
30234     initEvents: function() 
30235     {
30236         Roo.bootstrap.Element.superclass.initEvents.call(this);
30237         
30238         this.indicator = this.indicatorEl();
30239         
30240         if(this.indicator){
30241             this.indicator.removeClass('visible');
30242             this.indicator.addClass('invisible');
30243         }
30244         
30245         Roo.bootstrap.FieldLabel.register(this);
30246     },
30247     
30248     indicatorEl : function()
30249     {
30250         var indicator = this.el.select('i.roo-required-indicator',true).first();
30251         
30252         if(!indicator){
30253             return false;
30254         }
30255         
30256         return indicator;
30257         
30258     },
30259     
30260     /**
30261      * Mark this field as valid
30262      */
30263     markValid : function()
30264     {
30265         if(this.indicator){
30266             this.indicator.removeClass('visible');
30267             this.indicator.addClass('invisible');
30268         }
30269         
30270         this.el.removeClass(this.invalidClass);
30271         
30272         this.el.addClass(this.validClass);
30273         
30274         this.fireEvent('valid', this);
30275     },
30276     
30277     /**
30278      * Mark this field as invalid
30279      * @param {String} msg The validation message
30280      */
30281     markInvalid : function(msg)
30282     {
30283         if(this.indicator){
30284             this.indicator.removeClass('invisible');
30285             this.indicator.addClass('visible');
30286         }
30287         
30288         this.el.removeClass(this.validClass);
30289         
30290         this.el.addClass(this.invalidClass);
30291         
30292         this.fireEvent('invalid', this, msg);
30293     }
30294     
30295    
30296 });
30297
30298 Roo.apply(Roo.bootstrap.FieldLabel, {
30299     
30300     groups: {},
30301     
30302      /**
30303     * register a FieldLabel Group
30304     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30305     */
30306     register : function(label)
30307     {
30308         if(this.groups.hasOwnProperty(label.target)){
30309             return;
30310         }
30311      
30312         this.groups[label.target] = label;
30313         
30314     },
30315     /**
30316     * fetch a FieldLabel Group based on the target
30317     * @param {string} target
30318     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30319     */
30320     get: function(target) {
30321         if (typeof(this.groups[target]) == 'undefined') {
30322             return false;
30323         }
30324         
30325         return this.groups[target] ;
30326     }
30327 });
30328
30329  
30330
30331  /*
30332  * - LGPL
30333  *
30334  * page DateSplitField.
30335  * 
30336  */
30337
30338
30339 /**
30340  * @class Roo.bootstrap.DateSplitField
30341  * @extends Roo.bootstrap.Component
30342  * Bootstrap DateSplitField class
30343  * @cfg {string} fieldLabel - the label associated
30344  * @cfg {Number} labelWidth set the width of label (0-12)
30345  * @cfg {String} labelAlign (top|left)
30346  * @cfg {Boolean} dayAllowBlank (true|false) default false
30347  * @cfg {Boolean} monthAllowBlank (true|false) default false
30348  * @cfg {Boolean} yearAllowBlank (true|false) default false
30349  * @cfg {string} dayPlaceholder 
30350  * @cfg {string} monthPlaceholder
30351  * @cfg {string} yearPlaceholder
30352  * @cfg {string} dayFormat default 'd'
30353  * @cfg {string} monthFormat default 'm'
30354  * @cfg {string} yearFormat default 'Y'
30355  * @cfg {Number} labellg set the width of label (1-12)
30356  * @cfg {Number} labelmd set the width of label (1-12)
30357  * @cfg {Number} labelsm set the width of label (1-12)
30358  * @cfg {Number} labelxs set the width of label (1-12)
30359
30360  *     
30361  * @constructor
30362  * Create a new DateSplitField
30363  * @param {Object} config The config object
30364  */
30365
30366 Roo.bootstrap.DateSplitField = function(config){
30367     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30368     
30369     this.addEvents({
30370         // raw events
30371          /**
30372          * @event years
30373          * getting the data of years
30374          * @param {Roo.bootstrap.DateSplitField} this
30375          * @param {Object} years
30376          */
30377         "years" : true,
30378         /**
30379          * @event days
30380          * getting the data of days
30381          * @param {Roo.bootstrap.DateSplitField} this
30382          * @param {Object} days
30383          */
30384         "days" : true,
30385         /**
30386          * @event invalid
30387          * Fires after the field has been marked as invalid.
30388          * @param {Roo.form.Field} this
30389          * @param {String} msg The validation message
30390          */
30391         invalid : true,
30392        /**
30393          * @event valid
30394          * Fires after the field has been validated with no errors.
30395          * @param {Roo.form.Field} this
30396          */
30397         valid : true
30398     });
30399 };
30400
30401 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30402     
30403     fieldLabel : '',
30404     labelAlign : 'top',
30405     labelWidth : 3,
30406     dayAllowBlank : false,
30407     monthAllowBlank : false,
30408     yearAllowBlank : false,
30409     dayPlaceholder : '',
30410     monthPlaceholder : '',
30411     yearPlaceholder : '',
30412     dayFormat : 'd',
30413     monthFormat : 'm',
30414     yearFormat : 'Y',
30415     isFormField : true,
30416     labellg : 0,
30417     labelmd : 0,
30418     labelsm : 0,
30419     labelxs : 0,
30420     
30421     getAutoCreate : function()
30422     {
30423         var cfg = {
30424             tag : 'div',
30425             cls : 'row roo-date-split-field-group',
30426             cn : [
30427                 {
30428                     tag : 'input',
30429                     type : 'hidden',
30430                     cls : 'form-hidden-field roo-date-split-field-group-value',
30431                     name : this.name
30432                 }
30433             ]
30434         };
30435         
30436         var labelCls = 'col-md-12';
30437         var contentCls = 'col-md-4';
30438         
30439         if(this.fieldLabel){
30440             
30441             var label = {
30442                 tag : 'div',
30443                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30444                 cn : [
30445                     {
30446                         tag : 'label',
30447                         html : this.fieldLabel
30448                     }
30449                 ]
30450             };
30451             
30452             if(this.labelAlign == 'left'){
30453             
30454                 if(this.labelWidth > 12){
30455                     label.style = "width: " + this.labelWidth + 'px';
30456                 }
30457
30458                 if(this.labelWidth < 13 && this.labelmd == 0){
30459                     this.labelmd = this.labelWidth;
30460                 }
30461
30462                 if(this.labellg > 0){
30463                     labelCls = ' col-lg-' + this.labellg;
30464                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30465                 }
30466
30467                 if(this.labelmd > 0){
30468                     labelCls = ' col-md-' + this.labelmd;
30469                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30470                 }
30471
30472                 if(this.labelsm > 0){
30473                     labelCls = ' col-sm-' + this.labelsm;
30474                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30475                 }
30476
30477                 if(this.labelxs > 0){
30478                     labelCls = ' col-xs-' + this.labelxs;
30479                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30480                 }
30481             }
30482             
30483             label.cls += ' ' + labelCls;
30484             
30485             cfg.cn.push(label);
30486         }
30487         
30488         Roo.each(['day', 'month', 'year'], function(t){
30489             cfg.cn.push({
30490                 tag : 'div',
30491                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30492             });
30493         }, this);
30494         
30495         return cfg;
30496     },
30497     
30498     inputEl: function ()
30499     {
30500         return this.el.select('.roo-date-split-field-group-value', true).first();
30501     },
30502     
30503     onRender : function(ct, position) 
30504     {
30505         var _this = this;
30506         
30507         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30508         
30509         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30510         
30511         this.dayField = new Roo.bootstrap.ComboBox({
30512             allowBlank : this.dayAllowBlank,
30513             alwaysQuery : true,
30514             displayField : 'value',
30515             editable : false,
30516             fieldLabel : '',
30517             forceSelection : true,
30518             mode : 'local',
30519             placeholder : this.dayPlaceholder,
30520             selectOnFocus : true,
30521             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30522             triggerAction : 'all',
30523             typeAhead : true,
30524             valueField : 'value',
30525             store : new Roo.data.SimpleStore({
30526                 data : (function() {    
30527                     var days = [];
30528                     _this.fireEvent('days', _this, days);
30529                     return days;
30530                 })(),
30531                 fields : [ 'value' ]
30532             }),
30533             listeners : {
30534                 select : function (_self, record, index)
30535                 {
30536                     _this.setValue(_this.getValue());
30537                 }
30538             }
30539         });
30540
30541         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30542         
30543         this.monthField = new Roo.bootstrap.MonthField({
30544             after : '<i class=\"fa fa-calendar\"></i>',
30545             allowBlank : this.monthAllowBlank,
30546             placeholder : this.monthPlaceholder,
30547             readOnly : true,
30548             listeners : {
30549                 render : function (_self)
30550                 {
30551                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30552                         e.preventDefault();
30553                         _self.focus();
30554                     });
30555                 },
30556                 select : function (_self, oldvalue, newvalue)
30557                 {
30558                     _this.setValue(_this.getValue());
30559                 }
30560             }
30561         });
30562         
30563         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30564         
30565         this.yearField = new Roo.bootstrap.ComboBox({
30566             allowBlank : this.yearAllowBlank,
30567             alwaysQuery : true,
30568             displayField : 'value',
30569             editable : false,
30570             fieldLabel : '',
30571             forceSelection : true,
30572             mode : 'local',
30573             placeholder : this.yearPlaceholder,
30574             selectOnFocus : true,
30575             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30576             triggerAction : 'all',
30577             typeAhead : true,
30578             valueField : 'value',
30579             store : new Roo.data.SimpleStore({
30580                 data : (function() {
30581                     var years = [];
30582                     _this.fireEvent('years', _this, years);
30583                     return years;
30584                 })(),
30585                 fields : [ 'value' ]
30586             }),
30587             listeners : {
30588                 select : function (_self, record, index)
30589                 {
30590                     _this.setValue(_this.getValue());
30591                 }
30592             }
30593         });
30594
30595         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30596     },
30597     
30598     setValue : function(v, format)
30599     {
30600         this.inputEl.dom.value = v;
30601         
30602         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30603         
30604         var d = Date.parseDate(v, f);
30605         
30606         if(!d){
30607             this.validate();
30608             return;
30609         }
30610         
30611         this.setDay(d.format(this.dayFormat));
30612         this.setMonth(d.format(this.monthFormat));
30613         this.setYear(d.format(this.yearFormat));
30614         
30615         this.validate();
30616         
30617         return;
30618     },
30619     
30620     setDay : function(v)
30621     {
30622         this.dayField.setValue(v);
30623         this.inputEl.dom.value = this.getValue();
30624         this.validate();
30625         return;
30626     },
30627     
30628     setMonth : function(v)
30629     {
30630         this.monthField.setValue(v, true);
30631         this.inputEl.dom.value = this.getValue();
30632         this.validate();
30633         return;
30634     },
30635     
30636     setYear : function(v)
30637     {
30638         this.yearField.setValue(v);
30639         this.inputEl.dom.value = this.getValue();
30640         this.validate();
30641         return;
30642     },
30643     
30644     getDay : function()
30645     {
30646         return this.dayField.getValue();
30647     },
30648     
30649     getMonth : function()
30650     {
30651         return this.monthField.getValue();
30652     },
30653     
30654     getYear : function()
30655     {
30656         return this.yearField.getValue();
30657     },
30658     
30659     getValue : function()
30660     {
30661         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30662         
30663         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30664         
30665         return date;
30666     },
30667     
30668     reset : function()
30669     {
30670         this.setDay('');
30671         this.setMonth('');
30672         this.setYear('');
30673         this.inputEl.dom.value = '';
30674         this.validate();
30675         return;
30676     },
30677     
30678     validate : function()
30679     {
30680         var d = this.dayField.validate();
30681         var m = this.monthField.validate();
30682         var y = this.yearField.validate();
30683         
30684         var valid = true;
30685         
30686         if(
30687                 (!this.dayAllowBlank && !d) ||
30688                 (!this.monthAllowBlank && !m) ||
30689                 (!this.yearAllowBlank && !y)
30690         ){
30691             valid = false;
30692         }
30693         
30694         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30695             return valid;
30696         }
30697         
30698         if(valid){
30699             this.markValid();
30700             return valid;
30701         }
30702         
30703         this.markInvalid();
30704         
30705         return valid;
30706     },
30707     
30708     markValid : function()
30709     {
30710         
30711         var label = this.el.select('label', true).first();
30712         var icon = this.el.select('i.fa-star', true).first();
30713
30714         if(label && icon){
30715             icon.remove();
30716         }
30717         
30718         this.fireEvent('valid', this);
30719     },
30720     
30721      /**
30722      * Mark this field as invalid
30723      * @param {String} msg The validation message
30724      */
30725     markInvalid : function(msg)
30726     {
30727         
30728         var label = this.el.select('label', true).first();
30729         var icon = this.el.select('i.fa-star', true).first();
30730
30731         if(label && !icon){
30732             this.el.select('.roo-date-split-field-label', true).createChild({
30733                 tag : 'i',
30734                 cls : 'text-danger fa fa-lg fa-star',
30735                 tooltip : 'This field is required',
30736                 style : 'margin-right:5px;'
30737             }, label, true);
30738         }
30739         
30740         this.fireEvent('invalid', this, msg);
30741     },
30742     
30743     clearInvalid : function()
30744     {
30745         var label = this.el.select('label', true).first();
30746         var icon = this.el.select('i.fa-star', true).first();
30747
30748         if(label && icon){
30749             icon.remove();
30750         }
30751         
30752         this.fireEvent('valid', this);
30753     },
30754     
30755     getName: function()
30756     {
30757         return this.name;
30758     }
30759     
30760 });
30761
30762  /**
30763  *
30764  * This is based on 
30765  * http://masonry.desandro.com
30766  *
30767  * The idea is to render all the bricks based on vertical width...
30768  *
30769  * The original code extends 'outlayer' - we might need to use that....
30770  * 
30771  */
30772
30773
30774 /**
30775  * @class Roo.bootstrap.LayoutMasonry
30776  * @extends Roo.bootstrap.Component
30777  * Bootstrap Layout Masonry class
30778  * 
30779  * @constructor
30780  * Create a new Element
30781  * @param {Object} config The config object
30782  */
30783
30784 Roo.bootstrap.LayoutMasonry = function(config){
30785     
30786     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30787     
30788     this.bricks = [];
30789     
30790     Roo.bootstrap.LayoutMasonry.register(this);
30791     
30792     this.addEvents({
30793         // raw events
30794         /**
30795          * @event layout
30796          * Fire after layout the items
30797          * @param {Roo.bootstrap.LayoutMasonry} this
30798          * @param {Roo.EventObject} e
30799          */
30800         "layout" : true
30801     });
30802     
30803 };
30804
30805 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30806     
30807     /**
30808      * @cfg {Boolean} isLayoutInstant = no animation?
30809      */   
30810     isLayoutInstant : false, // needed?
30811    
30812     /**
30813      * @cfg {Number} boxWidth  width of the columns
30814      */   
30815     boxWidth : 450,
30816     
30817       /**
30818      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30819      */   
30820     boxHeight : 0,
30821     
30822     /**
30823      * @cfg {Number} padWidth padding below box..
30824      */   
30825     padWidth : 10, 
30826     
30827     /**
30828      * @cfg {Number} gutter gutter width..
30829      */   
30830     gutter : 10,
30831     
30832      /**
30833      * @cfg {Number} maxCols maximum number of columns
30834      */   
30835     
30836     maxCols: 0,
30837     
30838     /**
30839      * @cfg {Boolean} isAutoInitial defalut true
30840      */   
30841     isAutoInitial : true, 
30842     
30843     containerWidth: 0,
30844     
30845     /**
30846      * @cfg {Boolean} isHorizontal defalut false
30847      */   
30848     isHorizontal : false, 
30849
30850     currentSize : null,
30851     
30852     tag: 'div',
30853     
30854     cls: '',
30855     
30856     bricks: null, //CompositeElement
30857     
30858     cols : 1,
30859     
30860     _isLayoutInited : false,
30861     
30862 //    isAlternative : false, // only use for vertical layout...
30863     
30864     /**
30865      * @cfg {Number} alternativePadWidth padding below box..
30866      */   
30867     alternativePadWidth : 50,
30868     
30869     selectedBrick : [],
30870     
30871     getAutoCreate : function(){
30872         
30873         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30874         
30875         var cfg = {
30876             tag: this.tag,
30877             cls: 'blog-masonary-wrapper ' + this.cls,
30878             cn : {
30879                 cls : 'mas-boxes masonary'
30880             }
30881         };
30882         
30883         return cfg;
30884     },
30885     
30886     getChildContainer: function( )
30887     {
30888         if (this.boxesEl) {
30889             return this.boxesEl;
30890         }
30891         
30892         this.boxesEl = this.el.select('.mas-boxes').first();
30893         
30894         return this.boxesEl;
30895     },
30896     
30897     
30898     initEvents : function()
30899     {
30900         var _this = this;
30901         
30902         if(this.isAutoInitial){
30903             Roo.log('hook children rendered');
30904             this.on('childrenrendered', function() {
30905                 Roo.log('children rendered');
30906                 _this.initial();
30907             } ,this);
30908         }
30909     },
30910     
30911     initial : function()
30912     {
30913         this.selectedBrick = [];
30914         
30915         this.currentSize = this.el.getBox(true);
30916         
30917         Roo.EventManager.onWindowResize(this.resize, this); 
30918
30919         if(!this.isAutoInitial){
30920             this.layout();
30921             return;
30922         }
30923         
30924         this.layout();
30925         
30926         return;
30927         //this.layout.defer(500,this);
30928         
30929     },
30930     
30931     resize : function()
30932     {
30933         var cs = this.el.getBox(true);
30934         
30935         if (
30936                 this.currentSize.width == cs.width && 
30937                 this.currentSize.x == cs.x && 
30938                 this.currentSize.height == cs.height && 
30939                 this.currentSize.y == cs.y 
30940         ) {
30941             Roo.log("no change in with or X or Y");
30942             return;
30943         }
30944         
30945         this.currentSize = cs;
30946         
30947         this.layout();
30948         
30949     },
30950     
30951     layout : function()
30952     {   
30953         this._resetLayout();
30954         
30955         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30956         
30957         this.layoutItems( isInstant );
30958       
30959         this._isLayoutInited = true;
30960         
30961         this.fireEvent('layout', this);
30962         
30963     },
30964     
30965     _resetLayout : function()
30966     {
30967         if(this.isHorizontal){
30968             this.horizontalMeasureColumns();
30969             return;
30970         }
30971         
30972         this.verticalMeasureColumns();
30973         
30974     },
30975     
30976     verticalMeasureColumns : function()
30977     {
30978         this.getContainerWidth();
30979         
30980 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30981 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30982 //            return;
30983 //        }
30984         
30985         var boxWidth = this.boxWidth + this.padWidth;
30986         
30987         if(this.containerWidth < this.boxWidth){
30988             boxWidth = this.containerWidth
30989         }
30990         
30991         var containerWidth = this.containerWidth;
30992         
30993         var cols = Math.floor(containerWidth / boxWidth);
30994         
30995         this.cols = Math.max( cols, 1 );
30996         
30997         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30998         
30999         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31000         
31001         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31002         
31003         this.colWidth = boxWidth + avail - this.padWidth;
31004         
31005         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31006         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31007     },
31008     
31009     horizontalMeasureColumns : function()
31010     {
31011         this.getContainerWidth();
31012         
31013         var boxWidth = this.boxWidth;
31014         
31015         if(this.containerWidth < boxWidth){
31016             boxWidth = this.containerWidth;
31017         }
31018         
31019         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31020         
31021         this.el.setHeight(boxWidth);
31022         
31023     },
31024     
31025     getContainerWidth : function()
31026     {
31027         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31028     },
31029     
31030     layoutItems : function( isInstant )
31031     {
31032         Roo.log(this.bricks);
31033         
31034         var items = Roo.apply([], this.bricks);
31035         
31036         if(this.isHorizontal){
31037             this._horizontalLayoutItems( items , isInstant );
31038             return;
31039         }
31040         
31041 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31042 //            this._verticalAlternativeLayoutItems( items , isInstant );
31043 //            return;
31044 //        }
31045         
31046         this._verticalLayoutItems( items , isInstant );
31047         
31048     },
31049     
31050     _verticalLayoutItems : function ( items , isInstant)
31051     {
31052         if ( !items || !items.length ) {
31053             return;
31054         }
31055         
31056         var standard = [
31057             ['xs', 'xs', 'xs', 'tall'],
31058             ['xs', 'xs', 'tall'],
31059             ['xs', 'xs', 'sm'],
31060             ['xs', 'xs', 'xs'],
31061             ['xs', 'tall'],
31062             ['xs', 'sm'],
31063             ['xs', 'xs'],
31064             ['xs'],
31065             
31066             ['sm', 'xs', 'xs'],
31067             ['sm', 'xs'],
31068             ['sm'],
31069             
31070             ['tall', 'xs', 'xs', 'xs'],
31071             ['tall', 'xs', 'xs'],
31072             ['tall', 'xs'],
31073             ['tall']
31074             
31075         ];
31076         
31077         var queue = [];
31078         
31079         var boxes = [];
31080         
31081         var box = [];
31082         
31083         Roo.each(items, function(item, k){
31084             
31085             switch (item.size) {
31086                 // these layouts take up a full box,
31087                 case 'md' :
31088                 case 'md-left' :
31089                 case 'md-right' :
31090                 case 'wide' :
31091                     
31092                     if(box.length){
31093                         boxes.push(box);
31094                         box = [];
31095                     }
31096                     
31097                     boxes.push([item]);
31098                     
31099                     break;
31100                     
31101                 case 'xs' :
31102                 case 'sm' :
31103                 case 'tall' :
31104                     
31105                     box.push(item);
31106                     
31107                     break;
31108                 default :
31109                     break;
31110                     
31111             }
31112             
31113         }, this);
31114         
31115         if(box.length){
31116             boxes.push(box);
31117             box = [];
31118         }
31119         
31120         var filterPattern = function(box, length)
31121         {
31122             if(!box.length){
31123                 return;
31124             }
31125             
31126             var match = false;
31127             
31128             var pattern = box.slice(0, length);
31129             
31130             var format = [];
31131             
31132             Roo.each(pattern, function(i){
31133                 format.push(i.size);
31134             }, this);
31135             
31136             Roo.each(standard, function(s){
31137                 
31138                 if(String(s) != String(format)){
31139                     return;
31140                 }
31141                 
31142                 match = true;
31143                 return false;
31144                 
31145             }, this);
31146             
31147             if(!match && length == 1){
31148                 return;
31149             }
31150             
31151             if(!match){
31152                 filterPattern(box, length - 1);
31153                 return;
31154             }
31155                 
31156             queue.push(pattern);
31157
31158             box = box.slice(length, box.length);
31159
31160             filterPattern(box, 4);
31161
31162             return;
31163             
31164         }
31165         
31166         Roo.each(boxes, function(box, k){
31167             
31168             if(!box.length){
31169                 return;
31170             }
31171             
31172             if(box.length == 1){
31173                 queue.push(box);
31174                 return;
31175             }
31176             
31177             filterPattern(box, 4);
31178             
31179         }, this);
31180         
31181         this._processVerticalLayoutQueue( queue, isInstant );
31182         
31183     },
31184     
31185 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31186 //    {
31187 //        if ( !items || !items.length ) {
31188 //            return;
31189 //        }
31190 //
31191 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31192 //        
31193 //    },
31194     
31195     _horizontalLayoutItems : function ( items , isInstant)
31196     {
31197         if ( !items || !items.length || items.length < 3) {
31198             return;
31199         }
31200         
31201         items.reverse();
31202         
31203         var eItems = items.slice(0, 3);
31204         
31205         items = items.slice(3, items.length);
31206         
31207         var standard = [
31208             ['xs', 'xs', 'xs', 'wide'],
31209             ['xs', 'xs', 'wide'],
31210             ['xs', 'xs', 'sm'],
31211             ['xs', 'xs', 'xs'],
31212             ['xs', 'wide'],
31213             ['xs', 'sm'],
31214             ['xs', 'xs'],
31215             ['xs'],
31216             
31217             ['sm', 'xs', 'xs'],
31218             ['sm', 'xs'],
31219             ['sm'],
31220             
31221             ['wide', 'xs', 'xs', 'xs'],
31222             ['wide', 'xs', 'xs'],
31223             ['wide', 'xs'],
31224             ['wide'],
31225             
31226             ['wide-thin']
31227         ];
31228         
31229         var queue = [];
31230         
31231         var boxes = [];
31232         
31233         var box = [];
31234         
31235         Roo.each(items, function(item, k){
31236             
31237             switch (item.size) {
31238                 case 'md' :
31239                 case 'md-left' :
31240                 case 'md-right' :
31241                 case 'tall' :
31242                     
31243                     if(box.length){
31244                         boxes.push(box);
31245                         box = [];
31246                     }
31247                     
31248                     boxes.push([item]);
31249                     
31250                     break;
31251                     
31252                 case 'xs' :
31253                 case 'sm' :
31254                 case 'wide' :
31255                 case 'wide-thin' :
31256                     
31257                     box.push(item);
31258                     
31259                     break;
31260                 default :
31261                     break;
31262                     
31263             }
31264             
31265         }, this);
31266         
31267         if(box.length){
31268             boxes.push(box);
31269             box = [];
31270         }
31271         
31272         var filterPattern = function(box, length)
31273         {
31274             if(!box.length){
31275                 return;
31276             }
31277             
31278             var match = false;
31279             
31280             var pattern = box.slice(0, length);
31281             
31282             var format = [];
31283             
31284             Roo.each(pattern, function(i){
31285                 format.push(i.size);
31286             }, this);
31287             
31288             Roo.each(standard, function(s){
31289                 
31290                 if(String(s) != String(format)){
31291                     return;
31292                 }
31293                 
31294                 match = true;
31295                 return false;
31296                 
31297             }, this);
31298             
31299             if(!match && length == 1){
31300                 return;
31301             }
31302             
31303             if(!match){
31304                 filterPattern(box, length - 1);
31305                 return;
31306             }
31307                 
31308             queue.push(pattern);
31309
31310             box = box.slice(length, box.length);
31311
31312             filterPattern(box, 4);
31313
31314             return;
31315             
31316         }
31317         
31318         Roo.each(boxes, function(box, k){
31319             
31320             if(!box.length){
31321                 return;
31322             }
31323             
31324             if(box.length == 1){
31325                 queue.push(box);
31326                 return;
31327             }
31328             
31329             filterPattern(box, 4);
31330             
31331         }, this);
31332         
31333         
31334         var prune = [];
31335         
31336         var pos = this.el.getBox(true);
31337         
31338         var minX = pos.x;
31339         
31340         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31341         
31342         var hit_end = false;
31343         
31344         Roo.each(queue, function(box){
31345             
31346             if(hit_end){
31347                 
31348                 Roo.each(box, function(b){
31349                 
31350                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31351                     b.el.hide();
31352
31353                 }, this);
31354
31355                 return;
31356             }
31357             
31358             var mx = 0;
31359             
31360             Roo.each(box, function(b){
31361                 
31362                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31363                 b.el.show();
31364
31365                 mx = Math.max(mx, b.x);
31366                 
31367             }, this);
31368             
31369             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31370             
31371             if(maxX < minX){
31372                 
31373                 Roo.each(box, function(b){
31374                 
31375                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31376                     b.el.hide();
31377                     
31378                 }, this);
31379                 
31380                 hit_end = true;
31381                 
31382                 return;
31383             }
31384             
31385             prune.push(box);
31386             
31387         }, this);
31388         
31389         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31390     },
31391     
31392     /** Sets position of item in DOM
31393     * @param {Element} item
31394     * @param {Number} x - horizontal position
31395     * @param {Number} y - vertical position
31396     * @param {Boolean} isInstant - disables transitions
31397     */
31398     _processVerticalLayoutQueue : function( queue, isInstant )
31399     {
31400         var pos = this.el.getBox(true);
31401         var x = pos.x;
31402         var y = pos.y;
31403         var maxY = [];
31404         
31405         for (var i = 0; i < this.cols; i++){
31406             maxY[i] = pos.y;
31407         }
31408         
31409         Roo.each(queue, function(box, k){
31410             
31411             var col = k % this.cols;
31412             
31413             Roo.each(box, function(b,kk){
31414                 
31415                 b.el.position('absolute');
31416                 
31417                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31418                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31419                 
31420                 if(b.size == 'md-left' || b.size == 'md-right'){
31421                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31422                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31423                 }
31424                 
31425                 b.el.setWidth(width);
31426                 b.el.setHeight(height);
31427                 // iframe?
31428                 b.el.select('iframe',true).setSize(width,height);
31429                 
31430             }, this);
31431             
31432             for (var i = 0; i < this.cols; i++){
31433                 
31434                 if(maxY[i] < maxY[col]){
31435                     col = i;
31436                     continue;
31437                 }
31438                 
31439                 col = Math.min(col, i);
31440                 
31441             }
31442             
31443             x = pos.x + col * (this.colWidth + this.padWidth);
31444             
31445             y = maxY[col];
31446             
31447             var positions = [];
31448             
31449             switch (box.length){
31450                 case 1 :
31451                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31452                     break;
31453                 case 2 :
31454                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31455                     break;
31456                 case 3 :
31457                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31458                     break;
31459                 case 4 :
31460                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31461                     break;
31462                 default :
31463                     break;
31464             }
31465             
31466             Roo.each(box, function(b,kk){
31467                 
31468                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31469                 
31470                 var sz = b.el.getSize();
31471                 
31472                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31473                 
31474             }, this);
31475             
31476         }, this);
31477         
31478         var mY = 0;
31479         
31480         for (var i = 0; i < this.cols; i++){
31481             mY = Math.max(mY, maxY[i]);
31482         }
31483         
31484         this.el.setHeight(mY - pos.y);
31485         
31486     },
31487     
31488 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31489 //    {
31490 //        var pos = this.el.getBox(true);
31491 //        var x = pos.x;
31492 //        var y = pos.y;
31493 //        var maxX = pos.right;
31494 //        
31495 //        var maxHeight = 0;
31496 //        
31497 //        Roo.each(items, function(item, k){
31498 //            
31499 //            var c = k % 2;
31500 //            
31501 //            item.el.position('absolute');
31502 //                
31503 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31504 //
31505 //            item.el.setWidth(width);
31506 //
31507 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31508 //
31509 //            item.el.setHeight(height);
31510 //            
31511 //            if(c == 0){
31512 //                item.el.setXY([x, y], isInstant ? false : true);
31513 //            } else {
31514 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31515 //            }
31516 //            
31517 //            y = y + height + this.alternativePadWidth;
31518 //            
31519 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31520 //            
31521 //        }, this);
31522 //        
31523 //        this.el.setHeight(maxHeight);
31524 //        
31525 //    },
31526     
31527     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31528     {
31529         var pos = this.el.getBox(true);
31530         
31531         var minX = pos.x;
31532         var minY = pos.y;
31533         
31534         var maxX = pos.right;
31535         
31536         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31537         
31538         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31539         
31540         Roo.each(queue, function(box, k){
31541             
31542             Roo.each(box, function(b, kk){
31543                 
31544                 b.el.position('absolute');
31545                 
31546                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31547                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31548                 
31549                 if(b.size == 'md-left' || b.size == 'md-right'){
31550                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31551                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31552                 }
31553                 
31554                 b.el.setWidth(width);
31555                 b.el.setHeight(height);
31556                 
31557             }, this);
31558             
31559             if(!box.length){
31560                 return;
31561             }
31562             
31563             var positions = [];
31564             
31565             switch (box.length){
31566                 case 1 :
31567                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31568                     break;
31569                 case 2 :
31570                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31571                     break;
31572                 case 3 :
31573                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31574                     break;
31575                 case 4 :
31576                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31577                     break;
31578                 default :
31579                     break;
31580             }
31581             
31582             Roo.each(box, function(b,kk){
31583                 
31584                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31585                 
31586                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31587                 
31588             }, this);
31589             
31590         }, this);
31591         
31592     },
31593     
31594     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31595     {
31596         Roo.each(eItems, function(b,k){
31597             
31598             b.size = (k == 0) ? 'sm' : 'xs';
31599             b.x = (k == 0) ? 2 : 1;
31600             b.y = (k == 0) ? 2 : 1;
31601             
31602             b.el.position('absolute');
31603             
31604             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31605                 
31606             b.el.setWidth(width);
31607             
31608             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31609             
31610             b.el.setHeight(height);
31611             
31612         }, this);
31613
31614         var positions = [];
31615         
31616         positions.push({
31617             x : maxX - this.unitWidth * 2 - this.gutter,
31618             y : minY
31619         });
31620         
31621         positions.push({
31622             x : maxX - this.unitWidth,
31623             y : minY + (this.unitWidth + this.gutter) * 2
31624         });
31625         
31626         positions.push({
31627             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31628             y : minY
31629         });
31630         
31631         Roo.each(eItems, function(b,k){
31632             
31633             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31634
31635         }, this);
31636         
31637     },
31638     
31639     getVerticalOneBoxColPositions : function(x, y, box)
31640     {
31641         var pos = [];
31642         
31643         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31644         
31645         if(box[0].size == 'md-left'){
31646             rand = 0;
31647         }
31648         
31649         if(box[0].size == 'md-right'){
31650             rand = 1;
31651         }
31652         
31653         pos.push({
31654             x : x + (this.unitWidth + this.gutter) * rand,
31655             y : y
31656         });
31657         
31658         return pos;
31659     },
31660     
31661     getVerticalTwoBoxColPositions : function(x, y, box)
31662     {
31663         var pos = [];
31664         
31665         if(box[0].size == 'xs'){
31666             
31667             pos.push({
31668                 x : x,
31669                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31670             });
31671
31672             pos.push({
31673                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31674                 y : y
31675             });
31676             
31677             return pos;
31678             
31679         }
31680         
31681         pos.push({
31682             x : x,
31683             y : y
31684         });
31685
31686         pos.push({
31687             x : x + (this.unitWidth + this.gutter) * 2,
31688             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31689         });
31690         
31691         return pos;
31692         
31693     },
31694     
31695     getVerticalThreeBoxColPositions : function(x, y, box)
31696     {
31697         var pos = [];
31698         
31699         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31700             
31701             pos.push({
31702                 x : x,
31703                 y : y
31704             });
31705
31706             pos.push({
31707                 x : x + (this.unitWidth + this.gutter) * 1,
31708                 y : y
31709             });
31710             
31711             pos.push({
31712                 x : x + (this.unitWidth + this.gutter) * 2,
31713                 y : y
31714             });
31715             
31716             return pos;
31717             
31718         }
31719         
31720         if(box[0].size == 'xs' && box[1].size == 'xs'){
31721             
31722             pos.push({
31723                 x : x,
31724                 y : y
31725             });
31726
31727             pos.push({
31728                 x : x,
31729                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31730             });
31731             
31732             pos.push({
31733                 x : x + (this.unitWidth + this.gutter) * 1,
31734                 y : y
31735             });
31736             
31737             return pos;
31738             
31739         }
31740         
31741         pos.push({
31742             x : x,
31743             y : y
31744         });
31745
31746         pos.push({
31747             x : x + (this.unitWidth + this.gutter) * 2,
31748             y : y
31749         });
31750
31751         pos.push({
31752             x : x + (this.unitWidth + this.gutter) * 2,
31753             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31754         });
31755             
31756         return pos;
31757         
31758     },
31759     
31760     getVerticalFourBoxColPositions : function(x, y, box)
31761     {
31762         var pos = [];
31763         
31764         if(box[0].size == 'xs'){
31765             
31766             pos.push({
31767                 x : x,
31768                 y : y
31769             });
31770
31771             pos.push({
31772                 x : x,
31773                 y : y + (this.unitHeight + this.gutter) * 1
31774             });
31775             
31776             pos.push({
31777                 x : x,
31778                 y : y + (this.unitHeight + this.gutter) * 2
31779             });
31780             
31781             pos.push({
31782                 x : x + (this.unitWidth + this.gutter) * 1,
31783                 y : y
31784             });
31785             
31786             return pos;
31787             
31788         }
31789         
31790         pos.push({
31791             x : x,
31792             y : y
31793         });
31794
31795         pos.push({
31796             x : x + (this.unitWidth + this.gutter) * 2,
31797             y : y
31798         });
31799
31800         pos.push({
31801             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31802             y : y + (this.unitHeight + this.gutter) * 1
31803         });
31804
31805         pos.push({
31806             x : x + (this.unitWidth + this.gutter) * 2,
31807             y : y + (this.unitWidth + this.gutter) * 2
31808         });
31809
31810         return pos;
31811         
31812     },
31813     
31814     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31815     {
31816         var pos = [];
31817         
31818         if(box[0].size == 'md-left'){
31819             pos.push({
31820                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31821                 y : minY
31822             });
31823             
31824             return pos;
31825         }
31826         
31827         if(box[0].size == 'md-right'){
31828             pos.push({
31829                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31830                 y : minY + (this.unitWidth + this.gutter) * 1
31831             });
31832             
31833             return pos;
31834         }
31835         
31836         var rand = Math.floor(Math.random() * (4 - box[0].y));
31837         
31838         pos.push({
31839             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31840             y : minY + (this.unitWidth + this.gutter) * rand
31841         });
31842         
31843         return pos;
31844         
31845     },
31846     
31847     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31848     {
31849         var pos = [];
31850         
31851         if(box[0].size == 'xs'){
31852             
31853             pos.push({
31854                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31855                 y : minY
31856             });
31857
31858             pos.push({
31859                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31860                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31861             });
31862             
31863             return pos;
31864             
31865         }
31866         
31867         pos.push({
31868             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31869             y : minY
31870         });
31871
31872         pos.push({
31873             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31874             y : minY + (this.unitWidth + this.gutter) * 2
31875         });
31876         
31877         return pos;
31878         
31879     },
31880     
31881     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31882     {
31883         var pos = [];
31884         
31885         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31886             
31887             pos.push({
31888                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31889                 y : minY
31890             });
31891
31892             pos.push({
31893                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31894                 y : minY + (this.unitWidth + this.gutter) * 1
31895             });
31896             
31897             pos.push({
31898                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31899                 y : minY + (this.unitWidth + this.gutter) * 2
31900             });
31901             
31902             return pos;
31903             
31904         }
31905         
31906         if(box[0].size == 'xs' && box[1].size == 'xs'){
31907             
31908             pos.push({
31909                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31910                 y : minY
31911             });
31912
31913             pos.push({
31914                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31915                 y : minY
31916             });
31917             
31918             pos.push({
31919                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31920                 y : minY + (this.unitWidth + this.gutter) * 1
31921             });
31922             
31923             return pos;
31924             
31925         }
31926         
31927         pos.push({
31928             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31929             y : minY
31930         });
31931
31932         pos.push({
31933             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31934             y : minY + (this.unitWidth + this.gutter) * 2
31935         });
31936
31937         pos.push({
31938             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31939             y : minY + (this.unitWidth + this.gutter) * 2
31940         });
31941             
31942         return pos;
31943         
31944     },
31945     
31946     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31947     {
31948         var pos = [];
31949         
31950         if(box[0].size == 'xs'){
31951             
31952             pos.push({
31953                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31954                 y : minY
31955             });
31956
31957             pos.push({
31958                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31959                 y : minY
31960             });
31961             
31962             pos.push({
31963                 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),
31964                 y : minY
31965             });
31966             
31967             pos.push({
31968                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31969                 y : minY + (this.unitWidth + this.gutter) * 1
31970             });
31971             
31972             return pos;
31973             
31974         }
31975         
31976         pos.push({
31977             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31978             y : minY
31979         });
31980         
31981         pos.push({
31982             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31983             y : minY + (this.unitWidth + this.gutter) * 2
31984         });
31985         
31986         pos.push({
31987             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31988             y : minY + (this.unitWidth + this.gutter) * 2
31989         });
31990         
31991         pos.push({
31992             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),
31993             y : minY + (this.unitWidth + this.gutter) * 2
31994         });
31995
31996         return pos;
31997         
31998     },
31999     
32000     /**
32001     * remove a Masonry Brick
32002     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32003     */
32004     removeBrick : function(brick_id)
32005     {
32006         if (!brick_id) {
32007             return;
32008         }
32009         
32010         for (var i = 0; i<this.bricks.length; i++) {
32011             if (this.bricks[i].id == brick_id) {
32012                 this.bricks.splice(i,1);
32013                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32014                 this.initial();
32015             }
32016         }
32017     },
32018     
32019     /**
32020     * adds a Masonry Brick
32021     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32022     */
32023     addBrick : function(cfg)
32024     {
32025         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32026         //this.register(cn);
32027         cn.parentId = this.id;
32028         cn.render(this.el);
32029         return cn;
32030     },
32031     
32032     /**
32033     * register a Masonry Brick
32034     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32035     */
32036     
32037     register : function(brick)
32038     {
32039         this.bricks.push(brick);
32040         brick.masonryId = this.id;
32041     },
32042     
32043     /**
32044     * clear all the Masonry Brick
32045     */
32046     clearAll : function()
32047     {
32048         this.bricks = [];
32049         //this.getChildContainer().dom.innerHTML = "";
32050         this.el.dom.innerHTML = '';
32051     },
32052     
32053     getSelected : function()
32054     {
32055         if (!this.selectedBrick) {
32056             return false;
32057         }
32058         
32059         return this.selectedBrick;
32060     }
32061 });
32062
32063 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32064     
32065     groups: {},
32066      /**
32067     * register a Masonry Layout
32068     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32069     */
32070     
32071     register : function(layout)
32072     {
32073         this.groups[layout.id] = layout;
32074     },
32075     /**
32076     * fetch a  Masonry Layout based on the masonry layout ID
32077     * @param {string} the masonry layout to add
32078     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32079     */
32080     
32081     get: function(layout_id) {
32082         if (typeof(this.groups[layout_id]) == 'undefined') {
32083             return false;
32084         }
32085         return this.groups[layout_id] ;
32086     }
32087     
32088     
32089     
32090 });
32091
32092  
32093
32094  /**
32095  *
32096  * This is based on 
32097  * http://masonry.desandro.com
32098  *
32099  * The idea is to render all the bricks based on vertical width...
32100  *
32101  * The original code extends 'outlayer' - we might need to use that....
32102  * 
32103  */
32104
32105
32106 /**
32107  * @class Roo.bootstrap.LayoutMasonryAuto
32108  * @extends Roo.bootstrap.Component
32109  * Bootstrap Layout Masonry class
32110  * 
32111  * @constructor
32112  * Create a new Element
32113  * @param {Object} config The config object
32114  */
32115
32116 Roo.bootstrap.LayoutMasonryAuto = function(config){
32117     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32118 };
32119
32120 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32121     
32122       /**
32123      * @cfg {Boolean} isFitWidth  - resize the width..
32124      */   
32125     isFitWidth : false,  // options..
32126     /**
32127      * @cfg {Boolean} isOriginLeft = left align?
32128      */   
32129     isOriginLeft : true,
32130     /**
32131      * @cfg {Boolean} isOriginTop = top align?
32132      */   
32133     isOriginTop : false,
32134     /**
32135      * @cfg {Boolean} isLayoutInstant = no animation?
32136      */   
32137     isLayoutInstant : false, // needed?
32138     /**
32139      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32140      */   
32141     isResizingContainer : true,
32142     /**
32143      * @cfg {Number} columnWidth  width of the columns 
32144      */   
32145     
32146     columnWidth : 0,
32147     
32148     /**
32149      * @cfg {Number} maxCols maximum number of columns
32150      */   
32151     
32152     maxCols: 0,
32153     /**
32154      * @cfg {Number} padHeight padding below box..
32155      */   
32156     
32157     padHeight : 10, 
32158     
32159     /**
32160      * @cfg {Boolean} isAutoInitial defalut true
32161      */   
32162     
32163     isAutoInitial : true, 
32164     
32165     // private?
32166     gutter : 0,
32167     
32168     containerWidth: 0,
32169     initialColumnWidth : 0,
32170     currentSize : null,
32171     
32172     colYs : null, // array.
32173     maxY : 0,
32174     padWidth: 10,
32175     
32176     
32177     tag: 'div',
32178     cls: '',
32179     bricks: null, //CompositeElement
32180     cols : 0, // array?
32181     // element : null, // wrapped now this.el
32182     _isLayoutInited : null, 
32183     
32184     
32185     getAutoCreate : function(){
32186         
32187         var cfg = {
32188             tag: this.tag,
32189             cls: 'blog-masonary-wrapper ' + this.cls,
32190             cn : {
32191                 cls : 'mas-boxes masonary'
32192             }
32193         };
32194         
32195         return cfg;
32196     },
32197     
32198     getChildContainer: function( )
32199     {
32200         if (this.boxesEl) {
32201             return this.boxesEl;
32202         }
32203         
32204         this.boxesEl = this.el.select('.mas-boxes').first();
32205         
32206         return this.boxesEl;
32207     },
32208     
32209     
32210     initEvents : function()
32211     {
32212         var _this = this;
32213         
32214         if(this.isAutoInitial){
32215             Roo.log('hook children rendered');
32216             this.on('childrenrendered', function() {
32217                 Roo.log('children rendered');
32218                 _this.initial();
32219             } ,this);
32220         }
32221         
32222     },
32223     
32224     initial : function()
32225     {
32226         this.reloadItems();
32227
32228         this.currentSize = this.el.getBox(true);
32229
32230         /// was window resize... - let's see if this works..
32231         Roo.EventManager.onWindowResize(this.resize, this); 
32232
32233         if(!this.isAutoInitial){
32234             this.layout();
32235             return;
32236         }
32237         
32238         this.layout.defer(500,this);
32239     },
32240     
32241     reloadItems: function()
32242     {
32243         this.bricks = this.el.select('.masonry-brick', true);
32244         
32245         this.bricks.each(function(b) {
32246             //Roo.log(b.getSize());
32247             if (!b.attr('originalwidth')) {
32248                 b.attr('originalwidth',  b.getSize().width);
32249             }
32250             
32251         });
32252         
32253         Roo.log(this.bricks.elements.length);
32254     },
32255     
32256     resize : function()
32257     {
32258         Roo.log('resize');
32259         var cs = this.el.getBox(true);
32260         
32261         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32262             Roo.log("no change in with or X");
32263             return;
32264         }
32265         this.currentSize = cs;
32266         this.layout();
32267     },
32268     
32269     layout : function()
32270     {
32271          Roo.log('layout');
32272         this._resetLayout();
32273         //this._manageStamps();
32274       
32275         // don't animate first layout
32276         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32277         this.layoutItems( isInstant );
32278       
32279         // flag for initalized
32280         this._isLayoutInited = true;
32281     },
32282     
32283     layoutItems : function( isInstant )
32284     {
32285         //var items = this._getItemsForLayout( this.items );
32286         // original code supports filtering layout items.. we just ignore it..
32287         
32288         this._layoutItems( this.bricks , isInstant );
32289       
32290         this._postLayout();
32291     },
32292     _layoutItems : function ( items , isInstant)
32293     {
32294        //this.fireEvent( 'layout', this, items );
32295     
32296
32297         if ( !items || !items.elements.length ) {
32298           // no items, emit event with empty array
32299             return;
32300         }
32301
32302         var queue = [];
32303         items.each(function(item) {
32304             Roo.log("layout item");
32305             Roo.log(item);
32306             // get x/y object from method
32307             var position = this._getItemLayoutPosition( item );
32308             // enqueue
32309             position.item = item;
32310             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32311             queue.push( position );
32312         }, this);
32313       
32314         this._processLayoutQueue( queue );
32315     },
32316     /** Sets position of item in DOM
32317     * @param {Element} item
32318     * @param {Number} x - horizontal position
32319     * @param {Number} y - vertical position
32320     * @param {Boolean} isInstant - disables transitions
32321     */
32322     _processLayoutQueue : function( queue )
32323     {
32324         for ( var i=0, len = queue.length; i < len; i++ ) {
32325             var obj = queue[i];
32326             obj.item.position('absolute');
32327             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32328         }
32329     },
32330       
32331     
32332     /**
32333     * Any logic you want to do after each layout,
32334     * i.e. size the container
32335     */
32336     _postLayout : function()
32337     {
32338         this.resizeContainer();
32339     },
32340     
32341     resizeContainer : function()
32342     {
32343         if ( !this.isResizingContainer ) {
32344             return;
32345         }
32346         var size = this._getContainerSize();
32347         if ( size ) {
32348             this.el.setSize(size.width,size.height);
32349             this.boxesEl.setSize(size.width,size.height);
32350         }
32351     },
32352     
32353     
32354     
32355     _resetLayout : function()
32356     {
32357         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32358         this.colWidth = this.el.getWidth();
32359         //this.gutter = this.el.getWidth(); 
32360         
32361         this.measureColumns();
32362
32363         // reset column Y
32364         var i = this.cols;
32365         this.colYs = [];
32366         while (i--) {
32367             this.colYs.push( 0 );
32368         }
32369     
32370         this.maxY = 0;
32371     },
32372
32373     measureColumns : function()
32374     {
32375         this.getContainerWidth();
32376       // if columnWidth is 0, default to outerWidth of first item
32377         if ( !this.columnWidth ) {
32378             var firstItem = this.bricks.first();
32379             Roo.log(firstItem);
32380             this.columnWidth  = this.containerWidth;
32381             if (firstItem && firstItem.attr('originalwidth') ) {
32382                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32383             }
32384             // columnWidth fall back to item of first element
32385             Roo.log("set column width?");
32386                         this.initialColumnWidth = this.columnWidth  ;
32387
32388             // if first elem has no width, default to size of container
32389             
32390         }
32391         
32392         
32393         if (this.initialColumnWidth) {
32394             this.columnWidth = this.initialColumnWidth;
32395         }
32396         
32397         
32398             
32399         // column width is fixed at the top - however if container width get's smaller we should
32400         // reduce it...
32401         
32402         // this bit calcs how man columns..
32403             
32404         var columnWidth = this.columnWidth += this.gutter;
32405       
32406         // calculate columns
32407         var containerWidth = this.containerWidth + this.gutter;
32408         
32409         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32410         // fix rounding errors, typically with gutters
32411         var excess = columnWidth - containerWidth % columnWidth;
32412         
32413         
32414         // if overshoot is less than a pixel, round up, otherwise floor it
32415         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32416         cols = Math[ mathMethod ]( cols );
32417         this.cols = Math.max( cols, 1 );
32418         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32419         
32420          // padding positioning..
32421         var totalColWidth = this.cols * this.columnWidth;
32422         var padavail = this.containerWidth - totalColWidth;
32423         // so for 2 columns - we need 3 'pads'
32424         
32425         var padNeeded = (1+this.cols) * this.padWidth;
32426         
32427         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32428         
32429         this.columnWidth += padExtra
32430         //this.padWidth = Math.floor(padavail /  ( this.cols));
32431         
32432         // adjust colum width so that padding is fixed??
32433         
32434         // we have 3 columns ... total = width * 3
32435         // we have X left over... that should be used by 
32436         
32437         //if (this.expandC) {
32438             
32439         //}
32440         
32441         
32442         
32443     },
32444     
32445     getContainerWidth : function()
32446     {
32447        /* // container is parent if fit width
32448         var container = this.isFitWidth ? this.element.parentNode : this.element;
32449         // check that this.size and size are there
32450         // IE8 triggers resize on body size change, so they might not be
32451         
32452         var size = getSize( container );  //FIXME
32453         this.containerWidth = size && size.innerWidth; //FIXME
32454         */
32455          
32456         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32457         
32458     },
32459     
32460     _getItemLayoutPosition : function( item )  // what is item?
32461     {
32462         // we resize the item to our columnWidth..
32463       
32464         item.setWidth(this.columnWidth);
32465         item.autoBoxAdjust  = false;
32466         
32467         var sz = item.getSize();
32468  
32469         // how many columns does this brick span
32470         var remainder = this.containerWidth % this.columnWidth;
32471         
32472         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32473         // round if off by 1 pixel, otherwise use ceil
32474         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32475         colSpan = Math.min( colSpan, this.cols );
32476         
32477         // normally this should be '1' as we dont' currently allow multi width columns..
32478         
32479         var colGroup = this._getColGroup( colSpan );
32480         // get the minimum Y value from the columns
32481         var minimumY = Math.min.apply( Math, colGroup );
32482         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32483         
32484         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32485          
32486         // position the brick
32487         var position = {
32488             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32489             y: this.currentSize.y + minimumY + this.padHeight
32490         };
32491         
32492         Roo.log(position);
32493         // apply setHeight to necessary columns
32494         var setHeight = minimumY + sz.height + this.padHeight;
32495         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32496         
32497         var setSpan = this.cols + 1 - colGroup.length;
32498         for ( var i = 0; i < setSpan; i++ ) {
32499           this.colYs[ shortColIndex + i ] = setHeight ;
32500         }
32501       
32502         return position;
32503     },
32504     
32505     /**
32506      * @param {Number} colSpan - number of columns the element spans
32507      * @returns {Array} colGroup
32508      */
32509     _getColGroup : function( colSpan )
32510     {
32511         if ( colSpan < 2 ) {
32512           // if brick spans only one column, use all the column Ys
32513           return this.colYs;
32514         }
32515       
32516         var colGroup = [];
32517         // how many different places could this brick fit horizontally
32518         var groupCount = this.cols + 1 - colSpan;
32519         // for each group potential horizontal position
32520         for ( var i = 0; i < groupCount; i++ ) {
32521           // make an array of colY values for that one group
32522           var groupColYs = this.colYs.slice( i, i + colSpan );
32523           // and get the max value of the array
32524           colGroup[i] = Math.max.apply( Math, groupColYs );
32525         }
32526         return colGroup;
32527     },
32528     /*
32529     _manageStamp : function( stamp )
32530     {
32531         var stampSize =  stamp.getSize();
32532         var offset = stamp.getBox();
32533         // get the columns that this stamp affects
32534         var firstX = this.isOriginLeft ? offset.x : offset.right;
32535         var lastX = firstX + stampSize.width;
32536         var firstCol = Math.floor( firstX / this.columnWidth );
32537         firstCol = Math.max( 0, firstCol );
32538         
32539         var lastCol = Math.floor( lastX / this.columnWidth );
32540         // lastCol should not go over if multiple of columnWidth #425
32541         lastCol -= lastX % this.columnWidth ? 0 : 1;
32542         lastCol = Math.min( this.cols - 1, lastCol );
32543         
32544         // set colYs to bottom of the stamp
32545         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32546             stampSize.height;
32547             
32548         for ( var i = firstCol; i <= lastCol; i++ ) {
32549           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32550         }
32551     },
32552     */
32553     
32554     _getContainerSize : function()
32555     {
32556         this.maxY = Math.max.apply( Math, this.colYs );
32557         var size = {
32558             height: this.maxY
32559         };
32560       
32561         if ( this.isFitWidth ) {
32562             size.width = this._getContainerFitWidth();
32563         }
32564       
32565         return size;
32566     },
32567     
32568     _getContainerFitWidth : function()
32569     {
32570         var unusedCols = 0;
32571         // count unused columns
32572         var i = this.cols;
32573         while ( --i ) {
32574           if ( this.colYs[i] !== 0 ) {
32575             break;
32576           }
32577           unusedCols++;
32578         }
32579         // fit container to columns that have been used
32580         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32581     },
32582     
32583     needsResizeLayout : function()
32584     {
32585         var previousWidth = this.containerWidth;
32586         this.getContainerWidth();
32587         return previousWidth !== this.containerWidth;
32588     }
32589  
32590 });
32591
32592  
32593
32594  /*
32595  * - LGPL
32596  *
32597  * element
32598  * 
32599  */
32600
32601 /**
32602  * @class Roo.bootstrap.MasonryBrick
32603  * @extends Roo.bootstrap.Component
32604  * Bootstrap MasonryBrick class
32605  * 
32606  * @constructor
32607  * Create a new MasonryBrick
32608  * @param {Object} config The config object
32609  */
32610
32611 Roo.bootstrap.MasonryBrick = function(config){
32612     
32613     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32614     
32615     Roo.bootstrap.MasonryBrick.register(this);
32616     
32617     this.addEvents({
32618         // raw events
32619         /**
32620          * @event click
32621          * When a MasonryBrick is clcik
32622          * @param {Roo.bootstrap.MasonryBrick} this
32623          * @param {Roo.EventObject} e
32624          */
32625         "click" : true
32626     });
32627 };
32628
32629 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32630     
32631     /**
32632      * @cfg {String} title
32633      */   
32634     title : '',
32635     /**
32636      * @cfg {String} html
32637      */   
32638     html : '',
32639     /**
32640      * @cfg {String} bgimage
32641      */   
32642     bgimage : '',
32643     /**
32644      * @cfg {String} videourl
32645      */   
32646     videourl : '',
32647     /**
32648      * @cfg {String} cls
32649      */   
32650     cls : '',
32651     /**
32652      * @cfg {String} href
32653      */   
32654     href : '',
32655     /**
32656      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32657      */   
32658     size : 'xs',
32659     
32660     /**
32661      * @cfg {String} placetitle (center|bottom)
32662      */   
32663     placetitle : '',
32664     
32665     /**
32666      * @cfg {Boolean} isFitContainer defalut true
32667      */   
32668     isFitContainer : true, 
32669     
32670     /**
32671      * @cfg {Boolean} preventDefault defalut false
32672      */   
32673     preventDefault : false, 
32674     
32675     /**
32676      * @cfg {Boolean} inverse defalut false
32677      */   
32678     maskInverse : false, 
32679     
32680     getAutoCreate : function()
32681     {
32682         if(!this.isFitContainer){
32683             return this.getSplitAutoCreate();
32684         }
32685         
32686         var cls = 'masonry-brick masonry-brick-full';
32687         
32688         if(this.href.length){
32689             cls += ' masonry-brick-link';
32690         }
32691         
32692         if(this.bgimage.length){
32693             cls += ' masonry-brick-image';
32694         }
32695         
32696         if(this.maskInverse){
32697             cls += ' mask-inverse';
32698         }
32699         
32700         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32701             cls += ' enable-mask';
32702         }
32703         
32704         if(this.size){
32705             cls += ' masonry-' + this.size + '-brick';
32706         }
32707         
32708         if(this.placetitle.length){
32709             
32710             switch (this.placetitle) {
32711                 case 'center' :
32712                     cls += ' masonry-center-title';
32713                     break;
32714                 case 'bottom' :
32715                     cls += ' masonry-bottom-title';
32716                     break;
32717                 default:
32718                     break;
32719             }
32720             
32721         } else {
32722             if(!this.html.length && !this.bgimage.length){
32723                 cls += ' masonry-center-title';
32724             }
32725
32726             if(!this.html.length && this.bgimage.length){
32727                 cls += ' masonry-bottom-title';
32728             }
32729         }
32730         
32731         if(this.cls){
32732             cls += ' ' + this.cls;
32733         }
32734         
32735         var cfg = {
32736             tag: (this.href.length) ? 'a' : 'div',
32737             cls: cls,
32738             cn: [
32739                 {
32740                     tag: 'div',
32741                     cls: 'masonry-brick-mask'
32742                 },
32743                 {
32744                     tag: 'div',
32745                     cls: 'masonry-brick-paragraph',
32746                     cn: []
32747                 }
32748             ]
32749         };
32750         
32751         if(this.href.length){
32752             cfg.href = this.href;
32753         }
32754         
32755         var cn = cfg.cn[1].cn;
32756         
32757         if(this.title.length){
32758             cn.push({
32759                 tag: 'h4',
32760                 cls: 'masonry-brick-title',
32761                 html: this.title
32762             });
32763         }
32764         
32765         if(this.html.length){
32766             cn.push({
32767                 tag: 'p',
32768                 cls: 'masonry-brick-text',
32769                 html: this.html
32770             });
32771         }
32772         
32773         if (!this.title.length && !this.html.length) {
32774             cfg.cn[1].cls += ' hide';
32775         }
32776         
32777         if(this.bgimage.length){
32778             cfg.cn.push({
32779                 tag: 'img',
32780                 cls: 'masonry-brick-image-view',
32781                 src: this.bgimage
32782             });
32783         }
32784         
32785         if(this.videourl.length){
32786             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32787             // youtube support only?
32788             cfg.cn.push({
32789                 tag: 'iframe',
32790                 cls: 'masonry-brick-image-view',
32791                 src: vurl,
32792                 frameborder : 0,
32793                 allowfullscreen : true
32794             });
32795         }
32796         
32797         return cfg;
32798         
32799     },
32800     
32801     getSplitAutoCreate : function()
32802     {
32803         var cls = 'masonry-brick masonry-brick-split';
32804         
32805         if(this.href.length){
32806             cls += ' masonry-brick-link';
32807         }
32808         
32809         if(this.bgimage.length){
32810             cls += ' masonry-brick-image';
32811         }
32812         
32813         if(this.size){
32814             cls += ' masonry-' + this.size + '-brick';
32815         }
32816         
32817         switch (this.placetitle) {
32818             case 'center' :
32819                 cls += ' masonry-center-title';
32820                 break;
32821             case 'bottom' :
32822                 cls += ' masonry-bottom-title';
32823                 break;
32824             default:
32825                 if(!this.bgimage.length){
32826                     cls += ' masonry-center-title';
32827                 }
32828
32829                 if(this.bgimage.length){
32830                     cls += ' masonry-bottom-title';
32831                 }
32832                 break;
32833         }
32834         
32835         if(this.cls){
32836             cls += ' ' + this.cls;
32837         }
32838         
32839         var cfg = {
32840             tag: (this.href.length) ? 'a' : 'div',
32841             cls: cls,
32842             cn: [
32843                 {
32844                     tag: 'div',
32845                     cls: 'masonry-brick-split-head',
32846                     cn: [
32847                         {
32848                             tag: 'div',
32849                             cls: 'masonry-brick-paragraph',
32850                             cn: []
32851                         }
32852                     ]
32853                 },
32854                 {
32855                     tag: 'div',
32856                     cls: 'masonry-brick-split-body',
32857                     cn: []
32858                 }
32859             ]
32860         };
32861         
32862         if(this.href.length){
32863             cfg.href = this.href;
32864         }
32865         
32866         if(this.title.length){
32867             cfg.cn[0].cn[0].cn.push({
32868                 tag: 'h4',
32869                 cls: 'masonry-brick-title',
32870                 html: this.title
32871             });
32872         }
32873         
32874         if(this.html.length){
32875             cfg.cn[1].cn.push({
32876                 tag: 'p',
32877                 cls: 'masonry-brick-text',
32878                 html: this.html
32879             });
32880         }
32881
32882         if(this.bgimage.length){
32883             cfg.cn[0].cn.push({
32884                 tag: 'img',
32885                 cls: 'masonry-brick-image-view',
32886                 src: this.bgimage
32887             });
32888         }
32889         
32890         if(this.videourl.length){
32891             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32892             // youtube support only?
32893             cfg.cn[0].cn.cn.push({
32894                 tag: 'iframe',
32895                 cls: 'masonry-brick-image-view',
32896                 src: vurl,
32897                 frameborder : 0,
32898                 allowfullscreen : true
32899             });
32900         }
32901         
32902         return cfg;
32903     },
32904     
32905     initEvents: function() 
32906     {
32907         switch (this.size) {
32908             case 'xs' :
32909                 this.x = 1;
32910                 this.y = 1;
32911                 break;
32912             case 'sm' :
32913                 this.x = 2;
32914                 this.y = 2;
32915                 break;
32916             case 'md' :
32917             case 'md-left' :
32918             case 'md-right' :
32919                 this.x = 3;
32920                 this.y = 3;
32921                 break;
32922             case 'tall' :
32923                 this.x = 2;
32924                 this.y = 3;
32925                 break;
32926             case 'wide' :
32927                 this.x = 3;
32928                 this.y = 2;
32929                 break;
32930             case 'wide-thin' :
32931                 this.x = 3;
32932                 this.y = 1;
32933                 break;
32934                         
32935             default :
32936                 break;
32937         }
32938         
32939         if(Roo.isTouch){
32940             this.el.on('touchstart', this.onTouchStart, this);
32941             this.el.on('touchmove', this.onTouchMove, this);
32942             this.el.on('touchend', this.onTouchEnd, this);
32943             this.el.on('contextmenu', this.onContextMenu, this);
32944         } else {
32945             this.el.on('mouseenter'  ,this.enter, this);
32946             this.el.on('mouseleave', this.leave, this);
32947             this.el.on('click', this.onClick, this);
32948         }
32949         
32950         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32951             this.parent().bricks.push(this);   
32952         }
32953         
32954     },
32955     
32956     onClick: function(e, el)
32957     {
32958         var time = this.endTimer - this.startTimer;
32959         // Roo.log(e.preventDefault());
32960         if(Roo.isTouch){
32961             if(time > 1000){
32962                 e.preventDefault();
32963                 return;
32964             }
32965         }
32966         
32967         if(!this.preventDefault){
32968             return;
32969         }
32970         
32971         e.preventDefault();
32972         
32973         if (this.activeClass != '') {
32974             this.selectBrick();
32975         }
32976         
32977         this.fireEvent('click', this, e);
32978     },
32979     
32980     enter: function(e, el)
32981     {
32982         e.preventDefault();
32983         
32984         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32985             return;
32986         }
32987         
32988         if(this.bgimage.length && this.html.length){
32989             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32990         }
32991     },
32992     
32993     leave: function(e, el)
32994     {
32995         e.preventDefault();
32996         
32997         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32998             return;
32999         }
33000         
33001         if(this.bgimage.length && this.html.length){
33002             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33003         }
33004     },
33005     
33006     onTouchStart: function(e, el)
33007     {
33008 //        e.preventDefault();
33009         
33010         this.touchmoved = false;
33011         
33012         if(!this.isFitContainer){
33013             return;
33014         }
33015         
33016         if(!this.bgimage.length || !this.html.length){
33017             return;
33018         }
33019         
33020         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33021         
33022         this.timer = new Date().getTime();
33023         
33024     },
33025     
33026     onTouchMove: function(e, el)
33027     {
33028         this.touchmoved = true;
33029     },
33030     
33031     onContextMenu : function(e,el)
33032     {
33033         e.preventDefault();
33034         e.stopPropagation();
33035         return false;
33036     },
33037     
33038     onTouchEnd: function(e, el)
33039     {
33040 //        e.preventDefault();
33041         
33042         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33043         
33044             this.leave(e,el);
33045             
33046             return;
33047         }
33048         
33049         if(!this.bgimage.length || !this.html.length){
33050             
33051             if(this.href.length){
33052                 window.location.href = this.href;
33053             }
33054             
33055             return;
33056         }
33057         
33058         if(!this.isFitContainer){
33059             return;
33060         }
33061         
33062         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33063         
33064         window.location.href = this.href;
33065     },
33066     
33067     //selection on single brick only
33068     selectBrick : function() {
33069         
33070         if (!this.parentId) {
33071             return;
33072         }
33073         
33074         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33075         var index = m.selectedBrick.indexOf(this.id);
33076         
33077         if ( index > -1) {
33078             m.selectedBrick.splice(index,1);
33079             this.el.removeClass(this.activeClass);
33080             return;
33081         }
33082         
33083         for(var i = 0; i < m.selectedBrick.length; i++) {
33084             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33085             b.el.removeClass(b.activeClass);
33086         }
33087         
33088         m.selectedBrick = [];
33089         
33090         m.selectedBrick.push(this.id);
33091         this.el.addClass(this.activeClass);
33092         return;
33093     },
33094     
33095     isSelected : function(){
33096         return this.el.hasClass(this.activeClass);
33097         
33098     }
33099 });
33100
33101 Roo.apply(Roo.bootstrap.MasonryBrick, {
33102     
33103     //groups: {},
33104     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33105      /**
33106     * register a Masonry Brick
33107     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33108     */
33109     
33110     register : function(brick)
33111     {
33112         //this.groups[brick.id] = brick;
33113         this.groups.add(brick.id, brick);
33114     },
33115     /**
33116     * fetch a  masonry brick based on the masonry brick ID
33117     * @param {string} the masonry brick to add
33118     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33119     */
33120     
33121     get: function(brick_id) 
33122     {
33123         // if (typeof(this.groups[brick_id]) == 'undefined') {
33124         //     return false;
33125         // }
33126         // return this.groups[brick_id] ;
33127         
33128         if(this.groups.key(brick_id)) {
33129             return this.groups.key(brick_id);
33130         }
33131         
33132         return false;
33133     }
33134     
33135     
33136     
33137 });
33138
33139  /*
33140  * - LGPL
33141  *
33142  * element
33143  * 
33144  */
33145
33146 /**
33147  * @class Roo.bootstrap.Brick
33148  * @extends Roo.bootstrap.Component
33149  * Bootstrap Brick class
33150  * 
33151  * @constructor
33152  * Create a new Brick
33153  * @param {Object} config The config object
33154  */
33155
33156 Roo.bootstrap.Brick = function(config){
33157     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33158     
33159     this.addEvents({
33160         // raw events
33161         /**
33162          * @event click
33163          * When a Brick is click
33164          * @param {Roo.bootstrap.Brick} this
33165          * @param {Roo.EventObject} e
33166          */
33167         "click" : true
33168     });
33169 };
33170
33171 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33172     
33173     /**
33174      * @cfg {String} title
33175      */   
33176     title : '',
33177     /**
33178      * @cfg {String} html
33179      */   
33180     html : '',
33181     /**
33182      * @cfg {String} bgimage
33183      */   
33184     bgimage : '',
33185     /**
33186      * @cfg {String} cls
33187      */   
33188     cls : '',
33189     /**
33190      * @cfg {String} href
33191      */   
33192     href : '',
33193     /**
33194      * @cfg {String} video
33195      */   
33196     video : '',
33197     /**
33198      * @cfg {Boolean} square
33199      */   
33200     square : true,
33201     
33202     getAutoCreate : function()
33203     {
33204         var cls = 'roo-brick';
33205         
33206         if(this.href.length){
33207             cls += ' roo-brick-link';
33208         }
33209         
33210         if(this.bgimage.length){
33211             cls += ' roo-brick-image';
33212         }
33213         
33214         if(!this.html.length && !this.bgimage.length){
33215             cls += ' roo-brick-center-title';
33216         }
33217         
33218         if(!this.html.length && this.bgimage.length){
33219             cls += ' roo-brick-bottom-title';
33220         }
33221         
33222         if(this.cls){
33223             cls += ' ' + this.cls;
33224         }
33225         
33226         var cfg = {
33227             tag: (this.href.length) ? 'a' : 'div',
33228             cls: cls,
33229             cn: [
33230                 {
33231                     tag: 'div',
33232                     cls: 'roo-brick-paragraph',
33233                     cn: []
33234                 }
33235             ]
33236         };
33237         
33238         if(this.href.length){
33239             cfg.href = this.href;
33240         }
33241         
33242         var cn = cfg.cn[0].cn;
33243         
33244         if(this.title.length){
33245             cn.push({
33246                 tag: 'h4',
33247                 cls: 'roo-brick-title',
33248                 html: this.title
33249             });
33250         }
33251         
33252         if(this.html.length){
33253             cn.push({
33254                 tag: 'p',
33255                 cls: 'roo-brick-text',
33256                 html: this.html
33257             });
33258         } else {
33259             cn.cls += ' hide';
33260         }
33261         
33262         if(this.bgimage.length){
33263             cfg.cn.push({
33264                 tag: 'img',
33265                 cls: 'roo-brick-image-view',
33266                 src: this.bgimage
33267             });
33268         }
33269         
33270         return cfg;
33271     },
33272     
33273     initEvents: function() 
33274     {
33275         if(this.title.length || this.html.length){
33276             this.el.on('mouseenter'  ,this.enter, this);
33277             this.el.on('mouseleave', this.leave, this);
33278         }
33279         
33280         Roo.EventManager.onWindowResize(this.resize, this); 
33281         
33282         if(this.bgimage.length){
33283             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33284             this.imageEl.on('load', this.onImageLoad, this);
33285             return;
33286         }
33287         
33288         this.resize();
33289     },
33290     
33291     onImageLoad : function()
33292     {
33293         this.resize();
33294     },
33295     
33296     resize : function()
33297     {
33298         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33299         
33300         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33301         
33302         if(this.bgimage.length){
33303             var image = this.el.select('.roo-brick-image-view', true).first();
33304             
33305             image.setWidth(paragraph.getWidth());
33306             
33307             if(this.square){
33308                 image.setHeight(paragraph.getWidth());
33309             }
33310             
33311             this.el.setHeight(image.getHeight());
33312             paragraph.setHeight(image.getHeight());
33313             
33314         }
33315         
33316     },
33317     
33318     enter: function(e, el)
33319     {
33320         e.preventDefault();
33321         
33322         if(this.bgimage.length){
33323             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33324             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33325         }
33326     },
33327     
33328     leave: function(e, el)
33329     {
33330         e.preventDefault();
33331         
33332         if(this.bgimage.length){
33333             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33334             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33335         }
33336     }
33337     
33338 });
33339
33340  
33341
33342  /*
33343  * - LGPL
33344  *
33345  * Number field 
33346  */
33347
33348 /**
33349  * @class Roo.bootstrap.NumberField
33350  * @extends Roo.bootstrap.Input
33351  * Bootstrap NumberField class
33352  * 
33353  * 
33354  * 
33355  * 
33356  * @constructor
33357  * Create a new NumberField
33358  * @param {Object} config The config object
33359  */
33360
33361 Roo.bootstrap.NumberField = function(config){
33362     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33363 };
33364
33365 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33366     
33367     /**
33368      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33369      */
33370     allowDecimals : true,
33371     /**
33372      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33373      */
33374     decimalSeparator : ".",
33375     /**
33376      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33377      */
33378     decimalPrecision : 2,
33379     /**
33380      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33381      */
33382     allowNegative : true,
33383     
33384     /**
33385      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33386      */
33387     allowZero: true,
33388     /**
33389      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33390      */
33391     minValue : Number.NEGATIVE_INFINITY,
33392     /**
33393      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33394      */
33395     maxValue : Number.MAX_VALUE,
33396     /**
33397      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33398      */
33399     minText : "The minimum value for this field is {0}",
33400     /**
33401      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33402      */
33403     maxText : "The maximum value for this field is {0}",
33404     /**
33405      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33406      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33407      */
33408     nanText : "{0} is not a valid number",
33409     /**
33410      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33411      */
33412     thousandsDelimiter : false,
33413     /**
33414      * @cfg {String} valueAlign alignment of value
33415      */
33416     valueAlign : "left",
33417
33418     getAutoCreate : function()
33419     {
33420         var hiddenInput = {
33421             tag: 'input',
33422             type: 'hidden',
33423             id: Roo.id(),
33424             cls: 'hidden-number-input'
33425         };
33426         
33427         if (this.name) {
33428             hiddenInput.name = this.name;
33429         }
33430         
33431         this.name = '';
33432         
33433         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33434         
33435         this.name = hiddenInput.name;
33436         
33437         if(cfg.cn.length > 0) {
33438             cfg.cn.push(hiddenInput);
33439         }
33440         
33441         return cfg;
33442     },
33443
33444     // private
33445     initEvents : function()
33446     {   
33447         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33448         
33449         var allowed = "0123456789";
33450         
33451         if(this.allowDecimals){
33452             allowed += this.decimalSeparator;
33453         }
33454         
33455         if(this.allowNegative){
33456             allowed += "-";
33457         }
33458         
33459         if(this.thousandsDelimiter) {
33460             allowed += ",";
33461         }
33462         
33463         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33464         
33465         var keyPress = function(e){
33466             
33467             var k = e.getKey();
33468             
33469             var c = e.getCharCode();
33470             
33471             if(
33472                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33473                     allowed.indexOf(String.fromCharCode(c)) === -1
33474             ){
33475                 e.stopEvent();
33476                 return;
33477             }
33478             
33479             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33480                 return;
33481             }
33482             
33483             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33484                 e.stopEvent();
33485             }
33486         };
33487         
33488         this.el.on("keypress", keyPress, this);
33489     },
33490     
33491     validateValue : function(value)
33492     {
33493         
33494         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33495             return false;
33496         }
33497         
33498         var num = this.parseValue(value);
33499         
33500         if(isNaN(num)){
33501             this.markInvalid(String.format(this.nanText, value));
33502             return false;
33503         }
33504         
33505         if(num < this.minValue){
33506             this.markInvalid(String.format(this.minText, this.minValue));
33507             return false;
33508         }
33509         
33510         if(num > this.maxValue){
33511             this.markInvalid(String.format(this.maxText, this.maxValue));
33512             return false;
33513         }
33514         
33515         return true;
33516     },
33517
33518     getValue : function()
33519     {
33520         var v = this.hiddenEl().getValue();
33521         
33522         return this.fixPrecision(this.parseValue(v));
33523     },
33524
33525     parseValue : function(value)
33526     {
33527         if(this.thousandsDelimiter) {
33528             value += "";
33529             r = new RegExp(",", "g");
33530             value = value.replace(r, "");
33531         }
33532         
33533         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33534         return isNaN(value) ? '' : value;
33535     },
33536
33537     fixPrecision : function(value)
33538     {
33539         if(this.thousandsDelimiter) {
33540             value += "";
33541             r = new RegExp(",", "g");
33542             value = value.replace(r, "");
33543         }
33544         
33545         var nan = isNaN(value);
33546         
33547         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33548             return nan ? '' : value;
33549         }
33550         return parseFloat(value).toFixed(this.decimalPrecision);
33551     },
33552
33553     setValue : function(v)
33554     {
33555         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33556         
33557         this.value = v;
33558         
33559         if(this.rendered){
33560             
33561             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33562             
33563             this.inputEl().dom.value = (v == '') ? '' :
33564                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33565             
33566             if(!this.allowZero && v === '0') {
33567                 this.hiddenEl().dom.value = '';
33568                 this.inputEl().dom.value = '';
33569             }
33570             
33571             this.validate();
33572         }
33573     },
33574
33575     decimalPrecisionFcn : function(v)
33576     {
33577         return Math.floor(v);
33578     },
33579
33580     beforeBlur : function()
33581     {
33582         var v = this.parseValue(this.getRawValue());
33583         
33584         if(v || v === 0 || v === ''){
33585             this.setValue(v);
33586         }
33587     },
33588     
33589     hiddenEl : function()
33590     {
33591         return this.el.select('input.hidden-number-input',true).first();
33592     }
33593     
33594 });
33595
33596  
33597
33598 /*
33599 * Licence: LGPL
33600 */
33601
33602 /**
33603  * @class Roo.bootstrap.DocumentSlider
33604  * @extends Roo.bootstrap.Component
33605  * Bootstrap DocumentSlider class
33606  * 
33607  * @constructor
33608  * Create a new DocumentViewer
33609  * @param {Object} config The config object
33610  */
33611
33612 Roo.bootstrap.DocumentSlider = function(config){
33613     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33614     
33615     this.files = [];
33616     
33617     this.addEvents({
33618         /**
33619          * @event initial
33620          * Fire after initEvent
33621          * @param {Roo.bootstrap.DocumentSlider} this
33622          */
33623         "initial" : true,
33624         /**
33625          * @event update
33626          * Fire after update
33627          * @param {Roo.bootstrap.DocumentSlider} this
33628          */
33629         "update" : true,
33630         /**
33631          * @event click
33632          * Fire after click
33633          * @param {Roo.bootstrap.DocumentSlider} this
33634          */
33635         "click" : true
33636     });
33637 };
33638
33639 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33640     
33641     files : false,
33642     
33643     indicator : 0,
33644     
33645     getAutoCreate : function()
33646     {
33647         var cfg = {
33648             tag : 'div',
33649             cls : 'roo-document-slider',
33650             cn : [
33651                 {
33652                     tag : 'div',
33653                     cls : 'roo-document-slider-header',
33654                     cn : [
33655                         {
33656                             tag : 'div',
33657                             cls : 'roo-document-slider-header-title'
33658                         }
33659                     ]
33660                 },
33661                 {
33662                     tag : 'div',
33663                     cls : 'roo-document-slider-body',
33664                     cn : [
33665                         {
33666                             tag : 'div',
33667                             cls : 'roo-document-slider-prev',
33668                             cn : [
33669                                 {
33670                                     tag : 'i',
33671                                     cls : 'fa fa-chevron-left'
33672                                 }
33673                             ]
33674                         },
33675                         {
33676                             tag : 'div',
33677                             cls : 'roo-document-slider-thumb',
33678                             cn : [
33679                                 {
33680                                     tag : 'img',
33681                                     cls : 'roo-document-slider-image'
33682                                 }
33683                             ]
33684                         },
33685                         {
33686                             tag : 'div',
33687                             cls : 'roo-document-slider-next',
33688                             cn : [
33689                                 {
33690                                     tag : 'i',
33691                                     cls : 'fa fa-chevron-right'
33692                                 }
33693                             ]
33694                         }
33695                     ]
33696                 }
33697             ]
33698         };
33699         
33700         return cfg;
33701     },
33702     
33703     initEvents : function()
33704     {
33705         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33706         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33707         
33708         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33709         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33710         
33711         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33712         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33713         
33714         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33715         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33716         
33717         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33718         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33719         
33720         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33721         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33722         
33723         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33724         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33725         
33726         this.thumbEl.on('click', this.onClick, this);
33727         
33728         this.prevIndicator.on('click', this.prev, this);
33729         
33730         this.nextIndicator.on('click', this.next, this);
33731         
33732     },
33733     
33734     initial : function()
33735     {
33736         if(this.files.length){
33737             this.indicator = 1;
33738             this.update()
33739         }
33740         
33741         this.fireEvent('initial', this);
33742     },
33743     
33744     update : function()
33745     {
33746         this.imageEl.attr('src', this.files[this.indicator - 1]);
33747         
33748         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33749         
33750         this.prevIndicator.show();
33751         
33752         if(this.indicator == 1){
33753             this.prevIndicator.hide();
33754         }
33755         
33756         this.nextIndicator.show();
33757         
33758         if(this.indicator == this.files.length){
33759             this.nextIndicator.hide();
33760         }
33761         
33762         this.thumbEl.scrollTo('top');
33763         
33764         this.fireEvent('update', this);
33765     },
33766     
33767     onClick : function(e)
33768     {
33769         e.preventDefault();
33770         
33771         this.fireEvent('click', this);
33772     },
33773     
33774     prev : function(e)
33775     {
33776         e.preventDefault();
33777         
33778         this.indicator = Math.max(1, this.indicator - 1);
33779         
33780         this.update();
33781     },
33782     
33783     next : function(e)
33784     {
33785         e.preventDefault();
33786         
33787         this.indicator = Math.min(this.files.length, this.indicator + 1);
33788         
33789         this.update();
33790     }
33791 });
33792 /*
33793  * - LGPL
33794  *
33795  * RadioSet
33796  *
33797  *
33798  */
33799
33800 /**
33801  * @class Roo.bootstrap.RadioSet
33802  * @extends Roo.bootstrap.Input
33803  * Bootstrap RadioSet class
33804  * @cfg {String} indicatorpos (left|right) default left
33805  * @cfg {Boolean} inline (true|false) inline the element (default true)
33806  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33807  * @constructor
33808  * Create a new RadioSet
33809  * @param {Object} config The config object
33810  */
33811
33812 Roo.bootstrap.RadioSet = function(config){
33813     
33814     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33815     
33816     this.radioes = [];
33817     
33818     Roo.bootstrap.RadioSet.register(this);
33819     
33820     this.addEvents({
33821         /**
33822         * @event check
33823         * Fires when the element is checked or unchecked.
33824         * @param {Roo.bootstrap.RadioSet} this This radio
33825         * @param {Roo.bootstrap.Radio} item The checked item
33826         */
33827        check : true,
33828        /**
33829         * @event click
33830         * Fires when the element is click.
33831         * @param {Roo.bootstrap.RadioSet} this This radio set
33832         * @param {Roo.bootstrap.Radio} item The checked item
33833         * @param {Roo.EventObject} e The event object
33834         */
33835        click : true
33836     });
33837     
33838 };
33839
33840 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33841
33842     radioes : false,
33843     
33844     inline : true,
33845     
33846     weight : '',
33847     
33848     indicatorpos : 'left',
33849     
33850     getAutoCreate : function()
33851     {
33852         var label = {
33853             tag : 'label',
33854             cls : 'roo-radio-set-label',
33855             cn : [
33856                 {
33857                     tag : 'span',
33858                     html : this.fieldLabel
33859                 }
33860             ]
33861         };
33862         
33863         if(this.indicatorpos == 'left'){
33864             label.cn.unshift({
33865                 tag : 'i',
33866                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33867                 tooltip : 'This field is required'
33868             });
33869         } else {
33870             label.cn.push({
33871                 tag : 'i',
33872                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33873                 tooltip : 'This field is required'
33874             });
33875         }
33876         
33877         var items = {
33878             tag : 'div',
33879             cls : 'roo-radio-set-items'
33880         };
33881         
33882         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33883         
33884         if (align === 'left' && this.fieldLabel.length) {
33885             
33886             items = {
33887                 cls : "roo-radio-set-right", 
33888                 cn: [
33889                     items
33890                 ]
33891             };
33892             
33893             if(this.labelWidth > 12){
33894                 label.style = "width: " + this.labelWidth + 'px';
33895             }
33896             
33897             if(this.labelWidth < 13 && this.labelmd == 0){
33898                 this.labelmd = this.labelWidth;
33899             }
33900             
33901             if(this.labellg > 0){
33902                 label.cls += ' col-lg-' + this.labellg;
33903                 items.cls += ' col-lg-' + (12 - this.labellg);
33904             }
33905             
33906             if(this.labelmd > 0){
33907                 label.cls += ' col-md-' + this.labelmd;
33908                 items.cls += ' col-md-' + (12 - this.labelmd);
33909             }
33910             
33911             if(this.labelsm > 0){
33912                 label.cls += ' col-sm-' + this.labelsm;
33913                 items.cls += ' col-sm-' + (12 - this.labelsm);
33914             }
33915             
33916             if(this.labelxs > 0){
33917                 label.cls += ' col-xs-' + this.labelxs;
33918                 items.cls += ' col-xs-' + (12 - this.labelxs);
33919             }
33920         }
33921         
33922         var cfg = {
33923             tag : 'div',
33924             cls : 'roo-radio-set',
33925             cn : [
33926                 {
33927                     tag : 'input',
33928                     cls : 'roo-radio-set-input',
33929                     type : 'hidden',
33930                     name : this.name,
33931                     value : this.value ? this.value :  ''
33932                 },
33933                 label,
33934                 items
33935             ]
33936         };
33937         
33938         if(this.weight.length){
33939             cfg.cls += ' roo-radio-' + this.weight;
33940         }
33941         
33942         if(this.inline) {
33943             cfg.cls += ' roo-radio-set-inline';
33944         }
33945         
33946         var settings=this;
33947         ['xs','sm','md','lg'].map(function(size){
33948             if (settings[size]) {
33949                 cfg.cls += ' col-' + size + '-' + settings[size];
33950             }
33951         });
33952         
33953         return cfg;
33954         
33955     },
33956
33957     initEvents : function()
33958     {
33959         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33960         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33961         
33962         if(!this.fieldLabel.length){
33963             this.labelEl.hide();
33964         }
33965         
33966         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33967         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33968         
33969         this.indicator = this.indicatorEl();
33970         
33971         if(this.indicator){
33972             this.indicator.addClass('invisible');
33973         }
33974         
33975         this.originalValue = this.getValue();
33976         
33977     },
33978     
33979     inputEl: function ()
33980     {
33981         return this.el.select('.roo-radio-set-input', true).first();
33982     },
33983     
33984     getChildContainer : function()
33985     {
33986         return this.itemsEl;
33987     },
33988     
33989     register : function(item)
33990     {
33991         this.radioes.push(item);
33992         
33993     },
33994     
33995     validate : function()
33996     {   
33997         if(this.getVisibilityEl().hasClass('hidden')){
33998             return true;
33999         }
34000         
34001         var valid = false;
34002         
34003         Roo.each(this.radioes, function(i){
34004             if(!i.checked){
34005                 return;
34006             }
34007             
34008             valid = true;
34009             return false;
34010         });
34011         
34012         if(this.allowBlank) {
34013             return true;
34014         }
34015         
34016         if(this.disabled || valid){
34017             this.markValid();
34018             return true;
34019         }
34020         
34021         this.markInvalid();
34022         return false;
34023         
34024     },
34025     
34026     markValid : function()
34027     {
34028         if(this.labelEl.isVisible(true)){
34029             this.indicatorEl().removeClass('visible');
34030             this.indicatorEl().addClass('invisible');
34031         }
34032         
34033         this.el.removeClass([this.invalidClass, this.validClass]);
34034         this.el.addClass(this.validClass);
34035         
34036         this.fireEvent('valid', this);
34037     },
34038     
34039     markInvalid : function(msg)
34040     {
34041         if(this.allowBlank || this.disabled){
34042             return;
34043         }
34044         
34045         if(this.labelEl.isVisible(true)){
34046             this.indicatorEl().removeClass('invisible');
34047             this.indicatorEl().addClass('visible');
34048         }
34049         
34050         this.el.removeClass([this.invalidClass, this.validClass]);
34051         this.el.addClass(this.invalidClass);
34052         
34053         this.fireEvent('invalid', this, msg);
34054         
34055     },
34056     
34057     setValue : function(v, suppressEvent)
34058     {   
34059         if(this.value === v){
34060             return;
34061         }
34062         
34063         this.value = v;
34064         
34065         if(this.rendered){
34066             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34067         }
34068         
34069         Roo.each(this.radioes, function(i){
34070             i.checked = false;
34071             i.el.removeClass('checked');
34072         });
34073         
34074         Roo.each(this.radioes, function(i){
34075             
34076             if(i.value === v || i.value.toString() === v.toString()){
34077                 i.checked = true;
34078                 i.el.addClass('checked');
34079                 
34080                 if(suppressEvent !== true){
34081                     this.fireEvent('check', this, i);
34082                 }
34083                 
34084                 return false;
34085             }
34086             
34087         }, this);
34088         
34089         this.validate();
34090     },
34091     
34092     clearInvalid : function(){
34093         
34094         if(!this.el || this.preventMark){
34095             return;
34096         }
34097         
34098         this.el.removeClass([this.invalidClass]);
34099         
34100         this.fireEvent('valid', this);
34101     }
34102     
34103 });
34104
34105 Roo.apply(Roo.bootstrap.RadioSet, {
34106     
34107     groups: {},
34108     
34109     register : function(set)
34110     {
34111         this.groups[set.name] = set;
34112     },
34113     
34114     get: function(name) 
34115     {
34116         if (typeof(this.groups[name]) == 'undefined') {
34117             return false;
34118         }
34119         
34120         return this.groups[name] ;
34121     }
34122     
34123 });
34124 /*
34125  * Based on:
34126  * Ext JS Library 1.1.1
34127  * Copyright(c) 2006-2007, Ext JS, LLC.
34128  *
34129  * Originally Released Under LGPL - original licence link has changed is not relivant.
34130  *
34131  * Fork - LGPL
34132  * <script type="text/javascript">
34133  */
34134
34135
34136 /**
34137  * @class Roo.bootstrap.SplitBar
34138  * @extends Roo.util.Observable
34139  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34140  * <br><br>
34141  * Usage:
34142  * <pre><code>
34143 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34144                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34145 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34146 split.minSize = 100;
34147 split.maxSize = 600;
34148 split.animate = true;
34149 split.on('moved', splitterMoved);
34150 </code></pre>
34151  * @constructor
34152  * Create a new SplitBar
34153  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34154  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34155  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34156  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34157                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34158                         position of the SplitBar).
34159  */
34160 Roo.bootstrap.SplitBar = function(cfg){
34161     
34162     /** @private */
34163     
34164     //{
34165     //  dragElement : elm
34166     //  resizingElement: el,
34167         // optional..
34168     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34169     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34170         // existingProxy ???
34171     //}
34172     
34173     this.el = Roo.get(cfg.dragElement, true);
34174     this.el.dom.unselectable = "on";
34175     /** @private */
34176     this.resizingEl = Roo.get(cfg.resizingElement, true);
34177
34178     /**
34179      * @private
34180      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34181      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34182      * @type Number
34183      */
34184     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34185     
34186     /**
34187      * The minimum size of the resizing element. (Defaults to 0)
34188      * @type Number
34189      */
34190     this.minSize = 0;
34191     
34192     /**
34193      * The maximum size of the resizing element. (Defaults to 2000)
34194      * @type Number
34195      */
34196     this.maxSize = 2000;
34197     
34198     /**
34199      * Whether to animate the transition to the new size
34200      * @type Boolean
34201      */
34202     this.animate = false;
34203     
34204     /**
34205      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34206      * @type Boolean
34207      */
34208     this.useShim = false;
34209     
34210     /** @private */
34211     this.shim = null;
34212     
34213     if(!cfg.existingProxy){
34214         /** @private */
34215         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34216     }else{
34217         this.proxy = Roo.get(cfg.existingProxy).dom;
34218     }
34219     /** @private */
34220     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34221     
34222     /** @private */
34223     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34224     
34225     /** @private */
34226     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34227     
34228     /** @private */
34229     this.dragSpecs = {};
34230     
34231     /**
34232      * @private The adapter to use to positon and resize elements
34233      */
34234     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34235     this.adapter.init(this);
34236     
34237     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34238         /** @private */
34239         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34240         this.el.addClass("roo-splitbar-h");
34241     }else{
34242         /** @private */
34243         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34244         this.el.addClass("roo-splitbar-v");
34245     }
34246     
34247     this.addEvents({
34248         /**
34249          * @event resize
34250          * Fires when the splitter is moved (alias for {@link #event-moved})
34251          * @param {Roo.bootstrap.SplitBar} this
34252          * @param {Number} newSize the new width or height
34253          */
34254         "resize" : true,
34255         /**
34256          * @event moved
34257          * Fires when the splitter is moved
34258          * @param {Roo.bootstrap.SplitBar} this
34259          * @param {Number} newSize the new width or height
34260          */
34261         "moved" : true,
34262         /**
34263          * @event beforeresize
34264          * Fires before the splitter is dragged
34265          * @param {Roo.bootstrap.SplitBar} this
34266          */
34267         "beforeresize" : true,
34268
34269         "beforeapply" : true
34270     });
34271
34272     Roo.util.Observable.call(this);
34273 };
34274
34275 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34276     onStartProxyDrag : function(x, y){
34277         this.fireEvent("beforeresize", this);
34278         if(!this.overlay){
34279             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34280             o.unselectable();
34281             o.enableDisplayMode("block");
34282             // all splitbars share the same overlay
34283             Roo.bootstrap.SplitBar.prototype.overlay = o;
34284         }
34285         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34286         this.overlay.show();
34287         Roo.get(this.proxy).setDisplayed("block");
34288         var size = this.adapter.getElementSize(this);
34289         this.activeMinSize = this.getMinimumSize();;
34290         this.activeMaxSize = this.getMaximumSize();;
34291         var c1 = size - this.activeMinSize;
34292         var c2 = Math.max(this.activeMaxSize - size, 0);
34293         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34294             this.dd.resetConstraints();
34295             this.dd.setXConstraint(
34296                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34297                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34298             );
34299             this.dd.setYConstraint(0, 0);
34300         }else{
34301             this.dd.resetConstraints();
34302             this.dd.setXConstraint(0, 0);
34303             this.dd.setYConstraint(
34304                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34305                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34306             );
34307          }
34308         this.dragSpecs.startSize = size;
34309         this.dragSpecs.startPoint = [x, y];
34310         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34311     },
34312     
34313     /** 
34314      * @private Called after the drag operation by the DDProxy
34315      */
34316     onEndProxyDrag : function(e){
34317         Roo.get(this.proxy).setDisplayed(false);
34318         var endPoint = Roo.lib.Event.getXY(e);
34319         if(this.overlay){
34320             this.overlay.hide();
34321         }
34322         var newSize;
34323         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34324             newSize = this.dragSpecs.startSize + 
34325                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34326                     endPoint[0] - this.dragSpecs.startPoint[0] :
34327                     this.dragSpecs.startPoint[0] - endPoint[0]
34328                 );
34329         }else{
34330             newSize = this.dragSpecs.startSize + 
34331                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34332                     endPoint[1] - this.dragSpecs.startPoint[1] :
34333                     this.dragSpecs.startPoint[1] - endPoint[1]
34334                 );
34335         }
34336         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34337         if(newSize != this.dragSpecs.startSize){
34338             if(this.fireEvent('beforeapply', this, newSize) !== false){
34339                 this.adapter.setElementSize(this, newSize);
34340                 this.fireEvent("moved", this, newSize);
34341                 this.fireEvent("resize", this, newSize);
34342             }
34343         }
34344     },
34345     
34346     /**
34347      * Get the adapter this SplitBar uses
34348      * @return The adapter object
34349      */
34350     getAdapter : function(){
34351         return this.adapter;
34352     },
34353     
34354     /**
34355      * Set the adapter this SplitBar uses
34356      * @param {Object} adapter A SplitBar adapter object
34357      */
34358     setAdapter : function(adapter){
34359         this.adapter = adapter;
34360         this.adapter.init(this);
34361     },
34362     
34363     /**
34364      * Gets the minimum size for the resizing element
34365      * @return {Number} The minimum size
34366      */
34367     getMinimumSize : function(){
34368         return this.minSize;
34369     },
34370     
34371     /**
34372      * Sets the minimum size for the resizing element
34373      * @param {Number} minSize The minimum size
34374      */
34375     setMinimumSize : function(minSize){
34376         this.minSize = minSize;
34377     },
34378     
34379     /**
34380      * Gets the maximum size for the resizing element
34381      * @return {Number} The maximum size
34382      */
34383     getMaximumSize : function(){
34384         return this.maxSize;
34385     },
34386     
34387     /**
34388      * Sets the maximum size for the resizing element
34389      * @param {Number} maxSize The maximum size
34390      */
34391     setMaximumSize : function(maxSize){
34392         this.maxSize = maxSize;
34393     },
34394     
34395     /**
34396      * Sets the initialize size for the resizing element
34397      * @param {Number} size The initial size
34398      */
34399     setCurrentSize : function(size){
34400         var oldAnimate = this.animate;
34401         this.animate = false;
34402         this.adapter.setElementSize(this, size);
34403         this.animate = oldAnimate;
34404     },
34405     
34406     /**
34407      * Destroy this splitbar. 
34408      * @param {Boolean} removeEl True to remove the element
34409      */
34410     destroy : function(removeEl){
34411         if(this.shim){
34412             this.shim.remove();
34413         }
34414         this.dd.unreg();
34415         this.proxy.parentNode.removeChild(this.proxy);
34416         if(removeEl){
34417             this.el.remove();
34418         }
34419     }
34420 });
34421
34422 /**
34423  * @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.
34424  */
34425 Roo.bootstrap.SplitBar.createProxy = function(dir){
34426     var proxy = new Roo.Element(document.createElement("div"));
34427     proxy.unselectable();
34428     var cls = 'roo-splitbar-proxy';
34429     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34430     document.body.appendChild(proxy.dom);
34431     return proxy.dom;
34432 };
34433
34434 /** 
34435  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34436  * Default Adapter. It assumes the splitter and resizing element are not positioned
34437  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34438  */
34439 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34440 };
34441
34442 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34443     // do nothing for now
34444     init : function(s){
34445     
34446     },
34447     /**
34448      * Called before drag operations to get the current size of the resizing element. 
34449      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34450      */
34451      getElementSize : function(s){
34452         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34453             return s.resizingEl.getWidth();
34454         }else{
34455             return s.resizingEl.getHeight();
34456         }
34457     },
34458     
34459     /**
34460      * Called after drag operations to set the size of the resizing element.
34461      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34462      * @param {Number} newSize The new size to set
34463      * @param {Function} onComplete A function to be invoked when resizing is complete
34464      */
34465     setElementSize : function(s, newSize, onComplete){
34466         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34467             if(!s.animate){
34468                 s.resizingEl.setWidth(newSize);
34469                 if(onComplete){
34470                     onComplete(s, newSize);
34471                 }
34472             }else{
34473                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34474             }
34475         }else{
34476             
34477             if(!s.animate){
34478                 s.resizingEl.setHeight(newSize);
34479                 if(onComplete){
34480                     onComplete(s, newSize);
34481                 }
34482             }else{
34483                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34484             }
34485         }
34486     }
34487 };
34488
34489 /** 
34490  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34491  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34492  * Adapter that  moves the splitter element to align with the resized sizing element. 
34493  * Used with an absolute positioned SplitBar.
34494  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34495  * document.body, make sure you assign an id to the body element.
34496  */
34497 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34498     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34499     this.container = Roo.get(container);
34500 };
34501
34502 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34503     init : function(s){
34504         this.basic.init(s);
34505     },
34506     
34507     getElementSize : function(s){
34508         return this.basic.getElementSize(s);
34509     },
34510     
34511     setElementSize : function(s, newSize, onComplete){
34512         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34513     },
34514     
34515     moveSplitter : function(s){
34516         var yes = Roo.bootstrap.SplitBar;
34517         switch(s.placement){
34518             case yes.LEFT:
34519                 s.el.setX(s.resizingEl.getRight());
34520                 break;
34521             case yes.RIGHT:
34522                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34523                 break;
34524             case yes.TOP:
34525                 s.el.setY(s.resizingEl.getBottom());
34526                 break;
34527             case yes.BOTTOM:
34528                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34529                 break;
34530         }
34531     }
34532 };
34533
34534 /**
34535  * Orientation constant - Create a vertical SplitBar
34536  * @static
34537  * @type Number
34538  */
34539 Roo.bootstrap.SplitBar.VERTICAL = 1;
34540
34541 /**
34542  * Orientation constant - Create a horizontal SplitBar
34543  * @static
34544  * @type Number
34545  */
34546 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34547
34548 /**
34549  * Placement constant - The resizing element is to the left of the splitter element
34550  * @static
34551  * @type Number
34552  */
34553 Roo.bootstrap.SplitBar.LEFT = 1;
34554
34555 /**
34556  * Placement constant - The resizing element is to the right of the splitter element
34557  * @static
34558  * @type Number
34559  */
34560 Roo.bootstrap.SplitBar.RIGHT = 2;
34561
34562 /**
34563  * Placement constant - The resizing element is positioned above the splitter element
34564  * @static
34565  * @type Number
34566  */
34567 Roo.bootstrap.SplitBar.TOP = 3;
34568
34569 /**
34570  * Placement constant - The resizing element is positioned under splitter element
34571  * @static
34572  * @type Number
34573  */
34574 Roo.bootstrap.SplitBar.BOTTOM = 4;
34575 Roo.namespace("Roo.bootstrap.layout");/*
34576  * Based on:
34577  * Ext JS Library 1.1.1
34578  * Copyright(c) 2006-2007, Ext JS, LLC.
34579  *
34580  * Originally Released Under LGPL - original licence link has changed is not relivant.
34581  *
34582  * Fork - LGPL
34583  * <script type="text/javascript">
34584  */
34585
34586 /**
34587  * @class Roo.bootstrap.layout.Manager
34588  * @extends Roo.bootstrap.Component
34589  * Base class for layout managers.
34590  */
34591 Roo.bootstrap.layout.Manager = function(config)
34592 {
34593     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34594
34595
34596
34597
34598
34599     /** false to disable window resize monitoring @type Boolean */
34600     this.monitorWindowResize = true;
34601     this.regions = {};
34602     this.addEvents({
34603         /**
34604          * @event layout
34605          * Fires when a layout is performed.
34606          * @param {Roo.LayoutManager} this
34607          */
34608         "layout" : true,
34609         /**
34610          * @event regionresized
34611          * Fires when the user resizes a region.
34612          * @param {Roo.LayoutRegion} region The resized region
34613          * @param {Number} newSize The new size (width for east/west, height for north/south)
34614          */
34615         "regionresized" : true,
34616         /**
34617          * @event regioncollapsed
34618          * Fires when a region is collapsed.
34619          * @param {Roo.LayoutRegion} region The collapsed region
34620          */
34621         "regioncollapsed" : true,
34622         /**
34623          * @event regionexpanded
34624          * Fires when a region is expanded.
34625          * @param {Roo.LayoutRegion} region The expanded region
34626          */
34627         "regionexpanded" : true
34628     });
34629     this.updating = false;
34630
34631     if (config.el) {
34632         this.el = Roo.get(config.el);
34633         this.initEvents();
34634     }
34635
34636 };
34637
34638 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34639
34640
34641     regions : null,
34642
34643     monitorWindowResize : true,
34644
34645
34646     updating : false,
34647
34648
34649     onRender : function(ct, position)
34650     {
34651         if(!this.el){
34652             this.el = Roo.get(ct);
34653             this.initEvents();
34654         }
34655         //this.fireEvent('render',this);
34656     },
34657
34658
34659     initEvents: function()
34660     {
34661
34662
34663         // ie scrollbar fix
34664         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34665             document.body.scroll = "no";
34666         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34667             this.el.position('relative');
34668         }
34669         this.id = this.el.id;
34670         this.el.addClass("roo-layout-container");
34671         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34672         if(this.el.dom != document.body ) {
34673             this.el.on('resize', this.layout,this);
34674             this.el.on('show', this.layout,this);
34675         }
34676
34677     },
34678
34679     /**
34680      * Returns true if this layout is currently being updated
34681      * @return {Boolean}
34682      */
34683     isUpdating : function(){
34684         return this.updating;
34685     },
34686
34687     /**
34688      * Suspend the LayoutManager from doing auto-layouts while
34689      * making multiple add or remove calls
34690      */
34691     beginUpdate : function(){
34692         this.updating = true;
34693     },
34694
34695     /**
34696      * Restore auto-layouts and optionally disable the manager from performing a layout
34697      * @param {Boolean} noLayout true to disable a layout update
34698      */
34699     endUpdate : function(noLayout){
34700         this.updating = false;
34701         if(!noLayout){
34702             this.layout();
34703         }
34704     },
34705
34706     layout: function(){
34707         // abstract...
34708     },
34709
34710     onRegionResized : function(region, newSize){
34711         this.fireEvent("regionresized", region, newSize);
34712         this.layout();
34713     },
34714
34715     onRegionCollapsed : function(region){
34716         this.fireEvent("regioncollapsed", region);
34717     },
34718
34719     onRegionExpanded : function(region){
34720         this.fireEvent("regionexpanded", region);
34721     },
34722
34723     /**
34724      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34725      * performs box-model adjustments.
34726      * @return {Object} The size as an object {width: (the width), height: (the height)}
34727      */
34728     getViewSize : function()
34729     {
34730         var size;
34731         if(this.el.dom != document.body){
34732             size = this.el.getSize();
34733         }else{
34734             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34735         }
34736         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34737         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34738         return size;
34739     },
34740
34741     /**
34742      * Returns the Element this layout is bound to.
34743      * @return {Roo.Element}
34744      */
34745     getEl : function(){
34746         return this.el;
34747     },
34748
34749     /**
34750      * Returns the specified region.
34751      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34752      * @return {Roo.LayoutRegion}
34753      */
34754     getRegion : function(target){
34755         return this.regions[target.toLowerCase()];
34756     },
34757
34758     onWindowResize : function(){
34759         if(this.monitorWindowResize){
34760             this.layout();
34761         }
34762     }
34763 });
34764 /*
34765  * Based on:
34766  * Ext JS Library 1.1.1
34767  * Copyright(c) 2006-2007, Ext JS, LLC.
34768  *
34769  * Originally Released Under LGPL - original licence link has changed is not relivant.
34770  *
34771  * Fork - LGPL
34772  * <script type="text/javascript">
34773  */
34774 /**
34775  * @class Roo.bootstrap.layout.Border
34776  * @extends Roo.bootstrap.layout.Manager
34777  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34778  * please see: examples/bootstrap/nested.html<br><br>
34779  
34780 <b>The container the layout is rendered into can be either the body element or any other element.
34781 If it is not the body element, the container needs to either be an absolute positioned element,
34782 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34783 the container size if it is not the body element.</b>
34784
34785 * @constructor
34786 * Create a new Border
34787 * @param {Object} config Configuration options
34788  */
34789 Roo.bootstrap.layout.Border = function(config){
34790     config = config || {};
34791     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34792     
34793     
34794     
34795     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34796         if(config[region]){
34797             config[region].region = region;
34798             this.addRegion(config[region]);
34799         }
34800     },this);
34801     
34802 };
34803
34804 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34805
34806 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34807     /**
34808      * Creates and adds a new region if it doesn't already exist.
34809      * @param {String} target The target region key (north, south, east, west or center).
34810      * @param {Object} config The regions config object
34811      * @return {BorderLayoutRegion} The new region
34812      */
34813     addRegion : function(config)
34814     {
34815         if(!this.regions[config.region]){
34816             var r = this.factory(config);
34817             this.bindRegion(r);
34818         }
34819         return this.regions[config.region];
34820     },
34821
34822     // private (kinda)
34823     bindRegion : function(r){
34824         this.regions[r.config.region] = r;
34825         
34826         r.on("visibilitychange",    this.layout, this);
34827         r.on("paneladded",          this.layout, this);
34828         r.on("panelremoved",        this.layout, this);
34829         r.on("invalidated",         this.layout, this);
34830         r.on("resized",             this.onRegionResized, this);
34831         r.on("collapsed",           this.onRegionCollapsed, this);
34832         r.on("expanded",            this.onRegionExpanded, this);
34833     },
34834
34835     /**
34836      * Performs a layout update.
34837      */
34838     layout : function()
34839     {
34840         if(this.updating) {
34841             return;
34842         }
34843         
34844         // render all the rebions if they have not been done alreayd?
34845         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34846             if(this.regions[region] && !this.regions[region].bodyEl){
34847                 this.regions[region].onRender(this.el)
34848             }
34849         },this);
34850         
34851         var size = this.getViewSize();
34852         var w = size.width;
34853         var h = size.height;
34854         var centerW = w;
34855         var centerH = h;
34856         var centerY = 0;
34857         var centerX = 0;
34858         //var x = 0, y = 0;
34859
34860         var rs = this.regions;
34861         var north = rs["north"];
34862         var south = rs["south"]; 
34863         var west = rs["west"];
34864         var east = rs["east"];
34865         var center = rs["center"];
34866         //if(this.hideOnLayout){ // not supported anymore
34867             //c.el.setStyle("display", "none");
34868         //}
34869         if(north && north.isVisible()){
34870             var b = north.getBox();
34871             var m = north.getMargins();
34872             b.width = w - (m.left+m.right);
34873             b.x = m.left;
34874             b.y = m.top;
34875             centerY = b.height + b.y + m.bottom;
34876             centerH -= centerY;
34877             north.updateBox(this.safeBox(b));
34878         }
34879         if(south && south.isVisible()){
34880             var b = south.getBox();
34881             var m = south.getMargins();
34882             b.width = w - (m.left+m.right);
34883             b.x = m.left;
34884             var totalHeight = (b.height + m.top + m.bottom);
34885             b.y = h - totalHeight + m.top;
34886             centerH -= totalHeight;
34887             south.updateBox(this.safeBox(b));
34888         }
34889         if(west && west.isVisible()){
34890             var b = west.getBox();
34891             var m = west.getMargins();
34892             b.height = centerH - (m.top+m.bottom);
34893             b.x = m.left;
34894             b.y = centerY + m.top;
34895             var totalWidth = (b.width + m.left + m.right);
34896             centerX += totalWidth;
34897             centerW -= totalWidth;
34898             west.updateBox(this.safeBox(b));
34899         }
34900         if(east && east.isVisible()){
34901             var b = east.getBox();
34902             var m = east.getMargins();
34903             b.height = centerH - (m.top+m.bottom);
34904             var totalWidth = (b.width + m.left + m.right);
34905             b.x = w - totalWidth + m.left;
34906             b.y = centerY + m.top;
34907             centerW -= totalWidth;
34908             east.updateBox(this.safeBox(b));
34909         }
34910         if(center){
34911             var m = center.getMargins();
34912             var centerBox = {
34913                 x: centerX + m.left,
34914                 y: centerY + m.top,
34915                 width: centerW - (m.left+m.right),
34916                 height: centerH - (m.top+m.bottom)
34917             };
34918             //if(this.hideOnLayout){
34919                 //center.el.setStyle("display", "block");
34920             //}
34921             center.updateBox(this.safeBox(centerBox));
34922         }
34923         this.el.repaint();
34924         this.fireEvent("layout", this);
34925     },
34926
34927     // private
34928     safeBox : function(box){
34929         box.width = Math.max(0, box.width);
34930         box.height = Math.max(0, box.height);
34931         return box;
34932     },
34933
34934     /**
34935      * Adds a ContentPanel (or subclass) to this layout.
34936      * @param {String} target The target region key (north, south, east, west or center).
34937      * @param {Roo.ContentPanel} panel The panel to add
34938      * @return {Roo.ContentPanel} The added panel
34939      */
34940     add : function(target, panel){
34941          
34942         target = target.toLowerCase();
34943         return this.regions[target].add(panel);
34944     },
34945
34946     /**
34947      * Remove a ContentPanel (or subclass) to this layout.
34948      * @param {String} target The target region key (north, south, east, west or center).
34949      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34950      * @return {Roo.ContentPanel} The removed panel
34951      */
34952     remove : function(target, panel){
34953         target = target.toLowerCase();
34954         return this.regions[target].remove(panel);
34955     },
34956
34957     /**
34958      * Searches all regions for a panel with the specified id
34959      * @param {String} panelId
34960      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34961      */
34962     findPanel : function(panelId){
34963         var rs = this.regions;
34964         for(var target in rs){
34965             if(typeof rs[target] != "function"){
34966                 var p = rs[target].getPanel(panelId);
34967                 if(p){
34968                     return p;
34969                 }
34970             }
34971         }
34972         return null;
34973     },
34974
34975     /**
34976      * Searches all regions for a panel with the specified id and activates (shows) it.
34977      * @param {String/ContentPanel} panelId The panels id or the panel itself
34978      * @return {Roo.ContentPanel} The shown panel or null
34979      */
34980     showPanel : function(panelId) {
34981       var rs = this.regions;
34982       for(var target in rs){
34983          var r = rs[target];
34984          if(typeof r != "function"){
34985             if(r.hasPanel(panelId)){
34986                return r.showPanel(panelId);
34987             }
34988          }
34989       }
34990       return null;
34991    },
34992
34993    /**
34994      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34995      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34996      */
34997    /*
34998     restoreState : function(provider){
34999         if(!provider){
35000             provider = Roo.state.Manager;
35001         }
35002         var sm = new Roo.LayoutStateManager();
35003         sm.init(this, provider);
35004     },
35005 */
35006  
35007  
35008     /**
35009      * Adds a xtype elements to the layout.
35010      * <pre><code>
35011
35012 layout.addxtype({
35013        xtype : 'ContentPanel',
35014        region: 'west',
35015        items: [ .... ]
35016    }
35017 );
35018
35019 layout.addxtype({
35020         xtype : 'NestedLayoutPanel',
35021         region: 'west',
35022         layout: {
35023            center: { },
35024            west: { }   
35025         },
35026         items : [ ... list of content panels or nested layout panels.. ]
35027    }
35028 );
35029 </code></pre>
35030      * @param {Object} cfg Xtype definition of item to add.
35031      */
35032     addxtype : function(cfg)
35033     {
35034         // basically accepts a pannel...
35035         // can accept a layout region..!?!?
35036         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35037         
35038         
35039         // theory?  children can only be panels??
35040         
35041         //if (!cfg.xtype.match(/Panel$/)) {
35042         //    return false;
35043         //}
35044         var ret = false;
35045         
35046         if (typeof(cfg.region) == 'undefined') {
35047             Roo.log("Failed to add Panel, region was not set");
35048             Roo.log(cfg);
35049             return false;
35050         }
35051         var region = cfg.region;
35052         delete cfg.region;
35053         
35054           
35055         var xitems = [];
35056         if (cfg.items) {
35057             xitems = cfg.items;
35058             delete cfg.items;
35059         }
35060         var nb = false;
35061         
35062         switch(cfg.xtype) 
35063         {
35064             case 'Content':  // ContentPanel (el, cfg)
35065             case 'Scroll':  // ContentPanel (el, cfg)
35066             case 'View': 
35067                 cfg.autoCreate = true;
35068                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35069                 //} else {
35070                 //    var el = this.el.createChild();
35071                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35072                 //}
35073                 
35074                 this.add(region, ret);
35075                 break;
35076             
35077             /*
35078             case 'TreePanel': // our new panel!
35079                 cfg.el = this.el.createChild();
35080                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35081                 this.add(region, ret);
35082                 break;
35083             */
35084             
35085             case 'Nest': 
35086                 // create a new Layout (which is  a Border Layout...
35087                 
35088                 var clayout = cfg.layout;
35089                 clayout.el  = this.el.createChild();
35090                 clayout.items   = clayout.items  || [];
35091                 
35092                 delete cfg.layout;
35093                 
35094                 // replace this exitems with the clayout ones..
35095                 xitems = clayout.items;
35096                  
35097                 // force background off if it's in center...
35098                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35099                     cfg.background = false;
35100                 }
35101                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35102                 
35103                 
35104                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35105                 //console.log('adding nested layout panel '  + cfg.toSource());
35106                 this.add(region, ret);
35107                 nb = {}; /// find first...
35108                 break;
35109             
35110             case 'Grid':
35111                 
35112                 // needs grid and region
35113                 
35114                 //var el = this.getRegion(region).el.createChild();
35115                 /*
35116                  *var el = this.el.createChild();
35117                 // create the grid first...
35118                 cfg.grid.container = el;
35119                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35120                 */
35121                 
35122                 if (region == 'center' && this.active ) {
35123                     cfg.background = false;
35124                 }
35125                 
35126                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35127                 
35128                 this.add(region, ret);
35129                 /*
35130                 if (cfg.background) {
35131                     // render grid on panel activation (if panel background)
35132                     ret.on('activate', function(gp) {
35133                         if (!gp.grid.rendered) {
35134                     //        gp.grid.render(el);
35135                         }
35136                     });
35137                 } else {
35138                   //  cfg.grid.render(el);
35139                 }
35140                 */
35141                 break;
35142            
35143            
35144             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35145                 // it was the old xcomponent building that caused this before.
35146                 // espeically if border is the top element in the tree.
35147                 ret = this;
35148                 break; 
35149                 
35150                     
35151                 
35152                 
35153                 
35154             default:
35155                 /*
35156                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35157                     
35158                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35159                     this.add(region, ret);
35160                 } else {
35161                 */
35162                     Roo.log(cfg);
35163                     throw "Can not add '" + cfg.xtype + "' to Border";
35164                     return null;
35165              
35166                                 
35167              
35168         }
35169         this.beginUpdate();
35170         // add children..
35171         var region = '';
35172         var abn = {};
35173         Roo.each(xitems, function(i)  {
35174             region = nb && i.region ? i.region : false;
35175             
35176             var add = ret.addxtype(i);
35177            
35178             if (region) {
35179                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35180                 if (!i.background) {
35181                     abn[region] = nb[region] ;
35182                 }
35183             }
35184             
35185         });
35186         this.endUpdate();
35187
35188         // make the last non-background panel active..
35189         //if (nb) { Roo.log(abn); }
35190         if (nb) {
35191             
35192             for(var r in abn) {
35193                 region = this.getRegion(r);
35194                 if (region) {
35195                     // tried using nb[r], but it does not work..
35196                      
35197                     region.showPanel(abn[r]);
35198                    
35199                 }
35200             }
35201         }
35202         return ret;
35203         
35204     },
35205     
35206     
35207 // private
35208     factory : function(cfg)
35209     {
35210         
35211         var validRegions = Roo.bootstrap.layout.Border.regions;
35212
35213         var target = cfg.region;
35214         cfg.mgr = this;
35215         
35216         var r = Roo.bootstrap.layout;
35217         Roo.log(target);
35218         switch(target){
35219             case "north":
35220                 return new r.North(cfg);
35221             case "south":
35222                 return new r.South(cfg);
35223             case "east":
35224                 return new r.East(cfg);
35225             case "west":
35226                 return new r.West(cfg);
35227             case "center":
35228                 return new r.Center(cfg);
35229         }
35230         throw 'Layout region "'+target+'" not supported.';
35231     }
35232     
35233     
35234 });
35235  /*
35236  * Based on:
35237  * Ext JS Library 1.1.1
35238  * Copyright(c) 2006-2007, Ext JS, LLC.
35239  *
35240  * Originally Released Under LGPL - original licence link has changed is not relivant.
35241  *
35242  * Fork - LGPL
35243  * <script type="text/javascript">
35244  */
35245  
35246 /**
35247  * @class Roo.bootstrap.layout.Basic
35248  * @extends Roo.util.Observable
35249  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35250  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35251  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35252  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35253  * @cfg {string}   region  the region that it inhabits..
35254  * @cfg {bool}   skipConfig skip config?
35255  * 
35256
35257  */
35258 Roo.bootstrap.layout.Basic = function(config){
35259     
35260     this.mgr = config.mgr;
35261     
35262     this.position = config.region;
35263     
35264     var skipConfig = config.skipConfig;
35265     
35266     this.events = {
35267         /**
35268          * @scope Roo.BasicLayoutRegion
35269          */
35270         
35271         /**
35272          * @event beforeremove
35273          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35274          * @param {Roo.LayoutRegion} this
35275          * @param {Roo.ContentPanel} panel The panel
35276          * @param {Object} e The cancel event object
35277          */
35278         "beforeremove" : true,
35279         /**
35280          * @event invalidated
35281          * Fires when the layout for this region is changed.
35282          * @param {Roo.LayoutRegion} this
35283          */
35284         "invalidated" : true,
35285         /**
35286          * @event visibilitychange
35287          * Fires when this region is shown or hidden 
35288          * @param {Roo.LayoutRegion} this
35289          * @param {Boolean} visibility true or false
35290          */
35291         "visibilitychange" : true,
35292         /**
35293          * @event paneladded
35294          * Fires when a panel is added. 
35295          * @param {Roo.LayoutRegion} this
35296          * @param {Roo.ContentPanel} panel The panel
35297          */
35298         "paneladded" : true,
35299         /**
35300          * @event panelremoved
35301          * Fires when a panel is removed. 
35302          * @param {Roo.LayoutRegion} this
35303          * @param {Roo.ContentPanel} panel The panel
35304          */
35305         "panelremoved" : true,
35306         /**
35307          * @event beforecollapse
35308          * Fires when this region before collapse.
35309          * @param {Roo.LayoutRegion} this
35310          */
35311         "beforecollapse" : true,
35312         /**
35313          * @event collapsed
35314          * Fires when this region is collapsed.
35315          * @param {Roo.LayoutRegion} this
35316          */
35317         "collapsed" : true,
35318         /**
35319          * @event expanded
35320          * Fires when this region is expanded.
35321          * @param {Roo.LayoutRegion} this
35322          */
35323         "expanded" : true,
35324         /**
35325          * @event slideshow
35326          * Fires when this region is slid into view.
35327          * @param {Roo.LayoutRegion} this
35328          */
35329         "slideshow" : true,
35330         /**
35331          * @event slidehide
35332          * Fires when this region slides out of view. 
35333          * @param {Roo.LayoutRegion} this
35334          */
35335         "slidehide" : true,
35336         /**
35337          * @event panelactivated
35338          * Fires when a panel is activated. 
35339          * @param {Roo.LayoutRegion} this
35340          * @param {Roo.ContentPanel} panel The activated panel
35341          */
35342         "panelactivated" : true,
35343         /**
35344          * @event resized
35345          * Fires when the user resizes this region. 
35346          * @param {Roo.LayoutRegion} this
35347          * @param {Number} newSize The new size (width for east/west, height for north/south)
35348          */
35349         "resized" : true
35350     };
35351     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35352     this.panels = new Roo.util.MixedCollection();
35353     this.panels.getKey = this.getPanelId.createDelegate(this);
35354     this.box = null;
35355     this.activePanel = null;
35356     // ensure listeners are added...
35357     
35358     if (config.listeners || config.events) {
35359         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35360             listeners : config.listeners || {},
35361             events : config.events || {}
35362         });
35363     }
35364     
35365     if(skipConfig !== true){
35366         this.applyConfig(config);
35367     }
35368 };
35369
35370 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35371 {
35372     getPanelId : function(p){
35373         return p.getId();
35374     },
35375     
35376     applyConfig : function(config){
35377         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35378         this.config = config;
35379         
35380     },
35381     
35382     /**
35383      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35384      * the width, for horizontal (north, south) the height.
35385      * @param {Number} newSize The new width or height
35386      */
35387     resizeTo : function(newSize){
35388         var el = this.el ? this.el :
35389                  (this.activePanel ? this.activePanel.getEl() : null);
35390         if(el){
35391             switch(this.position){
35392                 case "east":
35393                 case "west":
35394                     el.setWidth(newSize);
35395                     this.fireEvent("resized", this, newSize);
35396                 break;
35397                 case "north":
35398                 case "south":
35399                     el.setHeight(newSize);
35400                     this.fireEvent("resized", this, newSize);
35401                 break;                
35402             }
35403         }
35404     },
35405     
35406     getBox : function(){
35407         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35408     },
35409     
35410     getMargins : function(){
35411         return this.margins;
35412     },
35413     
35414     updateBox : function(box){
35415         this.box = box;
35416         var el = this.activePanel.getEl();
35417         el.dom.style.left = box.x + "px";
35418         el.dom.style.top = box.y + "px";
35419         this.activePanel.setSize(box.width, box.height);
35420     },
35421     
35422     /**
35423      * Returns the container element for this region.
35424      * @return {Roo.Element}
35425      */
35426     getEl : function(){
35427         return this.activePanel;
35428     },
35429     
35430     /**
35431      * Returns true if this region is currently visible.
35432      * @return {Boolean}
35433      */
35434     isVisible : function(){
35435         return this.activePanel ? true : false;
35436     },
35437     
35438     setActivePanel : function(panel){
35439         panel = this.getPanel(panel);
35440         if(this.activePanel && this.activePanel != panel){
35441             this.activePanel.setActiveState(false);
35442             this.activePanel.getEl().setLeftTop(-10000,-10000);
35443         }
35444         this.activePanel = panel;
35445         panel.setActiveState(true);
35446         if(this.box){
35447             panel.setSize(this.box.width, this.box.height);
35448         }
35449         this.fireEvent("panelactivated", this, panel);
35450         this.fireEvent("invalidated");
35451     },
35452     
35453     /**
35454      * Show the specified panel.
35455      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35456      * @return {Roo.ContentPanel} The shown panel or null
35457      */
35458     showPanel : function(panel){
35459         panel = this.getPanel(panel);
35460         if(panel){
35461             this.setActivePanel(panel);
35462         }
35463         return panel;
35464     },
35465     
35466     /**
35467      * Get the active panel for this region.
35468      * @return {Roo.ContentPanel} The active panel or null
35469      */
35470     getActivePanel : function(){
35471         return this.activePanel;
35472     },
35473     
35474     /**
35475      * Add the passed ContentPanel(s)
35476      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35477      * @return {Roo.ContentPanel} The panel added (if only one was added)
35478      */
35479     add : function(panel){
35480         if(arguments.length > 1){
35481             for(var i = 0, len = arguments.length; i < len; i++) {
35482                 this.add(arguments[i]);
35483             }
35484             return null;
35485         }
35486         if(this.hasPanel(panel)){
35487             this.showPanel(panel);
35488             return panel;
35489         }
35490         var el = panel.getEl();
35491         if(el.dom.parentNode != this.mgr.el.dom){
35492             this.mgr.el.dom.appendChild(el.dom);
35493         }
35494         if(panel.setRegion){
35495             panel.setRegion(this);
35496         }
35497         this.panels.add(panel);
35498         el.setStyle("position", "absolute");
35499         if(!panel.background){
35500             this.setActivePanel(panel);
35501             if(this.config.initialSize && this.panels.getCount()==1){
35502                 this.resizeTo(this.config.initialSize);
35503             }
35504         }
35505         this.fireEvent("paneladded", this, panel);
35506         return panel;
35507     },
35508     
35509     /**
35510      * Returns true if the panel is in this region.
35511      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35512      * @return {Boolean}
35513      */
35514     hasPanel : function(panel){
35515         if(typeof panel == "object"){ // must be panel obj
35516             panel = panel.getId();
35517         }
35518         return this.getPanel(panel) ? true : false;
35519     },
35520     
35521     /**
35522      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35523      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35524      * @param {Boolean} preservePanel Overrides the config preservePanel option
35525      * @return {Roo.ContentPanel} The panel that was removed
35526      */
35527     remove : function(panel, preservePanel){
35528         panel = this.getPanel(panel);
35529         if(!panel){
35530             return null;
35531         }
35532         var e = {};
35533         this.fireEvent("beforeremove", this, panel, e);
35534         if(e.cancel === true){
35535             return null;
35536         }
35537         var panelId = panel.getId();
35538         this.panels.removeKey(panelId);
35539         return panel;
35540     },
35541     
35542     /**
35543      * Returns the panel specified or null if it's not in this region.
35544      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35545      * @return {Roo.ContentPanel}
35546      */
35547     getPanel : function(id){
35548         if(typeof id == "object"){ // must be panel obj
35549             return id;
35550         }
35551         return this.panels.get(id);
35552     },
35553     
35554     /**
35555      * Returns this regions position (north/south/east/west/center).
35556      * @return {String} 
35557      */
35558     getPosition: function(){
35559         return this.position;    
35560     }
35561 });/*
35562  * Based on:
35563  * Ext JS Library 1.1.1
35564  * Copyright(c) 2006-2007, Ext JS, LLC.
35565  *
35566  * Originally Released Under LGPL - original licence link has changed is not relivant.
35567  *
35568  * Fork - LGPL
35569  * <script type="text/javascript">
35570  */
35571  
35572 /**
35573  * @class Roo.bootstrap.layout.Region
35574  * @extends Roo.bootstrap.layout.Basic
35575  * This class represents a region in a layout manager.
35576  
35577  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35578  * @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})
35579  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35580  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35581  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35582  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35583  * @cfg {String}    title           The title for the region (overrides panel titles)
35584  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35585  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35586  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35587  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35588  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35589  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35590  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35591  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35592  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35593  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35594
35595  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35596  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35597  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35598  * @cfg {Number}    width           For East/West panels
35599  * @cfg {Number}    height          For North/South panels
35600  * @cfg {Boolean}   split           To show the splitter
35601  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35602  * 
35603  * @cfg {string}   cls             Extra CSS classes to add to region
35604  * 
35605  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35606  * @cfg {string}   region  the region that it inhabits..
35607  *
35608
35609  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35610  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35611
35612  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35613  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35614  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35615  */
35616 Roo.bootstrap.layout.Region = function(config)
35617 {
35618     this.applyConfig(config);
35619
35620     var mgr = config.mgr;
35621     var pos = config.region;
35622     config.skipConfig = true;
35623     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35624     
35625     if (mgr.el) {
35626         this.onRender(mgr.el);   
35627     }
35628      
35629     this.visible = true;
35630     this.collapsed = false;
35631     this.unrendered_panels = [];
35632 };
35633
35634 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35635
35636     position: '', // set by wrapper (eg. north/south etc..)
35637     unrendered_panels : null,  // unrendered panels.
35638     createBody : function(){
35639         /** This region's body element 
35640         * @type Roo.Element */
35641         this.bodyEl = this.el.createChild({
35642                 tag: "div",
35643                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35644         });
35645     },
35646
35647     onRender: function(ctr, pos)
35648     {
35649         var dh = Roo.DomHelper;
35650         /** This region's container element 
35651         * @type Roo.Element */
35652         this.el = dh.append(ctr.dom, {
35653                 tag: "div",
35654                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35655             }, true);
35656         /** This region's title element 
35657         * @type Roo.Element */
35658     
35659         this.titleEl = dh.append(this.el.dom,
35660             {
35661                     tag: "div",
35662                     unselectable: "on",
35663                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35664                     children:[
35665                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35666                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35667                     ]}, true);
35668         
35669         this.titleEl.enableDisplayMode();
35670         /** This region's title text element 
35671         * @type HTMLElement */
35672         this.titleTextEl = this.titleEl.dom.firstChild;
35673         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35674         /*
35675         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35676         this.closeBtn.enableDisplayMode();
35677         this.closeBtn.on("click", this.closeClicked, this);
35678         this.closeBtn.hide();
35679     */
35680         this.createBody(this.config);
35681         if(this.config.hideWhenEmpty){
35682             this.hide();
35683             this.on("paneladded", this.validateVisibility, this);
35684             this.on("panelremoved", this.validateVisibility, this);
35685         }
35686         if(this.autoScroll){
35687             this.bodyEl.setStyle("overflow", "auto");
35688         }else{
35689             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35690         }
35691         //if(c.titlebar !== false){
35692             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35693                 this.titleEl.hide();
35694             }else{
35695                 this.titleEl.show();
35696                 if(this.config.title){
35697                     this.titleTextEl.innerHTML = this.config.title;
35698                 }
35699             }
35700         //}
35701         if(this.config.collapsed){
35702             this.collapse(true);
35703         }
35704         if(this.config.hidden){
35705             this.hide();
35706         }
35707         
35708         if (this.unrendered_panels && this.unrendered_panels.length) {
35709             for (var i =0;i< this.unrendered_panels.length; i++) {
35710                 this.add(this.unrendered_panels[i]);
35711             }
35712             this.unrendered_panels = null;
35713             
35714         }
35715         
35716     },
35717     
35718     applyConfig : function(c)
35719     {
35720         /*
35721          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35722             var dh = Roo.DomHelper;
35723             if(c.titlebar !== false){
35724                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35725                 this.collapseBtn.on("click", this.collapse, this);
35726                 this.collapseBtn.enableDisplayMode();
35727                 /*
35728                 if(c.showPin === true || this.showPin){
35729                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35730                     this.stickBtn.enableDisplayMode();
35731                     this.stickBtn.on("click", this.expand, this);
35732                     this.stickBtn.hide();
35733                 }
35734                 
35735             }
35736             */
35737             /** This region's collapsed element
35738             * @type Roo.Element */
35739             /*
35740              *
35741             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35742                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35743             ]}, true);
35744             
35745             if(c.floatable !== false){
35746                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35747                this.collapsedEl.on("click", this.collapseClick, this);
35748             }
35749
35750             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35751                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35752                    id: "message", unselectable: "on", style:{"float":"left"}});
35753                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35754              }
35755             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35756             this.expandBtn.on("click", this.expand, this);
35757             
35758         }
35759         
35760         if(this.collapseBtn){
35761             this.collapseBtn.setVisible(c.collapsible == true);
35762         }
35763         
35764         this.cmargins = c.cmargins || this.cmargins ||
35765                          (this.position == "west" || this.position == "east" ?
35766                              {top: 0, left: 2, right:2, bottom: 0} :
35767                              {top: 2, left: 0, right:0, bottom: 2});
35768         */
35769         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35770         
35771         
35772         this.bottomTabs = c.tabPosition != "top";
35773         
35774         this.autoScroll = c.autoScroll || false;
35775         
35776         
35777        
35778         
35779         this.duration = c.duration || .30;
35780         this.slideDuration = c.slideDuration || .45;
35781         this.config = c;
35782        
35783     },
35784     /**
35785      * Returns true if this region is currently visible.
35786      * @return {Boolean}
35787      */
35788     isVisible : function(){
35789         return this.visible;
35790     },
35791
35792     /**
35793      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35794      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35795      */
35796     //setCollapsedTitle : function(title){
35797     //    title = title || "&#160;";
35798      //   if(this.collapsedTitleTextEl){
35799       //      this.collapsedTitleTextEl.innerHTML = title;
35800        // }
35801     //},
35802
35803     getBox : function(){
35804         var b;
35805       //  if(!this.collapsed){
35806             b = this.el.getBox(false, true);
35807        // }else{
35808           //  b = this.collapsedEl.getBox(false, true);
35809         //}
35810         return b;
35811     },
35812
35813     getMargins : function(){
35814         return this.margins;
35815         //return this.collapsed ? this.cmargins : this.margins;
35816     },
35817 /*
35818     highlight : function(){
35819         this.el.addClass("x-layout-panel-dragover");
35820     },
35821
35822     unhighlight : function(){
35823         this.el.removeClass("x-layout-panel-dragover");
35824     },
35825 */
35826     updateBox : function(box)
35827     {
35828         if (!this.bodyEl) {
35829             return; // not rendered yet..
35830         }
35831         
35832         this.box = box;
35833         if(!this.collapsed){
35834             this.el.dom.style.left = box.x + "px";
35835             this.el.dom.style.top = box.y + "px";
35836             this.updateBody(box.width, box.height);
35837         }else{
35838             this.collapsedEl.dom.style.left = box.x + "px";
35839             this.collapsedEl.dom.style.top = box.y + "px";
35840             this.collapsedEl.setSize(box.width, box.height);
35841         }
35842         if(this.tabs){
35843             this.tabs.autoSizeTabs();
35844         }
35845     },
35846
35847     updateBody : function(w, h)
35848     {
35849         if(w !== null){
35850             this.el.setWidth(w);
35851             w -= this.el.getBorderWidth("rl");
35852             if(this.config.adjustments){
35853                 w += this.config.adjustments[0];
35854             }
35855         }
35856         if(h !== null && h > 0){
35857             this.el.setHeight(h);
35858             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35859             h -= this.el.getBorderWidth("tb");
35860             if(this.config.adjustments){
35861                 h += this.config.adjustments[1];
35862             }
35863             this.bodyEl.setHeight(h);
35864             if(this.tabs){
35865                 h = this.tabs.syncHeight(h);
35866             }
35867         }
35868         if(this.panelSize){
35869             w = w !== null ? w : this.panelSize.width;
35870             h = h !== null ? h : this.panelSize.height;
35871         }
35872         if(this.activePanel){
35873             var el = this.activePanel.getEl();
35874             w = w !== null ? w : el.getWidth();
35875             h = h !== null ? h : el.getHeight();
35876             this.panelSize = {width: w, height: h};
35877             this.activePanel.setSize(w, h);
35878         }
35879         if(Roo.isIE && this.tabs){
35880             this.tabs.el.repaint();
35881         }
35882     },
35883
35884     /**
35885      * Returns the container element for this region.
35886      * @return {Roo.Element}
35887      */
35888     getEl : function(){
35889         return this.el;
35890     },
35891
35892     /**
35893      * Hides this region.
35894      */
35895     hide : function(){
35896         //if(!this.collapsed){
35897             this.el.dom.style.left = "-2000px";
35898             this.el.hide();
35899         //}else{
35900          //   this.collapsedEl.dom.style.left = "-2000px";
35901          //   this.collapsedEl.hide();
35902        // }
35903         this.visible = false;
35904         this.fireEvent("visibilitychange", this, false);
35905     },
35906
35907     /**
35908      * Shows this region if it was previously hidden.
35909      */
35910     show : function(){
35911         //if(!this.collapsed){
35912             this.el.show();
35913         //}else{
35914         //    this.collapsedEl.show();
35915        // }
35916         this.visible = true;
35917         this.fireEvent("visibilitychange", this, true);
35918     },
35919 /*
35920     closeClicked : function(){
35921         if(this.activePanel){
35922             this.remove(this.activePanel);
35923         }
35924     },
35925
35926     collapseClick : function(e){
35927         if(this.isSlid){
35928            e.stopPropagation();
35929            this.slideIn();
35930         }else{
35931            e.stopPropagation();
35932            this.slideOut();
35933         }
35934     },
35935 */
35936     /**
35937      * Collapses this region.
35938      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35939      */
35940     /*
35941     collapse : function(skipAnim, skipCheck = false){
35942         if(this.collapsed) {
35943             return;
35944         }
35945         
35946         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35947             
35948             this.collapsed = true;
35949             if(this.split){
35950                 this.split.el.hide();
35951             }
35952             if(this.config.animate && skipAnim !== true){
35953                 this.fireEvent("invalidated", this);
35954                 this.animateCollapse();
35955             }else{
35956                 this.el.setLocation(-20000,-20000);
35957                 this.el.hide();
35958                 this.collapsedEl.show();
35959                 this.fireEvent("collapsed", this);
35960                 this.fireEvent("invalidated", this);
35961             }
35962         }
35963         
35964     },
35965 */
35966     animateCollapse : function(){
35967         // overridden
35968     },
35969
35970     /**
35971      * Expands this region if it was previously collapsed.
35972      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35973      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35974      */
35975     /*
35976     expand : function(e, skipAnim){
35977         if(e) {
35978             e.stopPropagation();
35979         }
35980         if(!this.collapsed || this.el.hasActiveFx()) {
35981             return;
35982         }
35983         if(this.isSlid){
35984             this.afterSlideIn();
35985             skipAnim = true;
35986         }
35987         this.collapsed = false;
35988         if(this.config.animate && skipAnim !== true){
35989             this.animateExpand();
35990         }else{
35991             this.el.show();
35992             if(this.split){
35993                 this.split.el.show();
35994             }
35995             this.collapsedEl.setLocation(-2000,-2000);
35996             this.collapsedEl.hide();
35997             this.fireEvent("invalidated", this);
35998             this.fireEvent("expanded", this);
35999         }
36000     },
36001 */
36002     animateExpand : function(){
36003         // overridden
36004     },
36005
36006     initTabs : function()
36007     {
36008         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36009         
36010         var ts = new Roo.bootstrap.panel.Tabs({
36011                 el: this.bodyEl.dom,
36012                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36013                 disableTooltips: this.config.disableTabTips,
36014                 toolbar : this.config.toolbar
36015             });
36016         
36017         if(this.config.hideTabs){
36018             ts.stripWrap.setDisplayed(false);
36019         }
36020         this.tabs = ts;
36021         ts.resizeTabs = this.config.resizeTabs === true;
36022         ts.minTabWidth = this.config.minTabWidth || 40;
36023         ts.maxTabWidth = this.config.maxTabWidth || 250;
36024         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36025         ts.monitorResize = false;
36026         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36027         ts.bodyEl.addClass('roo-layout-tabs-body');
36028         this.panels.each(this.initPanelAsTab, this);
36029     },
36030
36031     initPanelAsTab : function(panel){
36032         var ti = this.tabs.addTab(
36033             panel.getEl().id,
36034             panel.getTitle(),
36035             null,
36036             this.config.closeOnTab && panel.isClosable(),
36037             panel.tpl
36038         );
36039         if(panel.tabTip !== undefined){
36040             ti.setTooltip(panel.tabTip);
36041         }
36042         ti.on("activate", function(){
36043               this.setActivePanel(panel);
36044         }, this);
36045         
36046         if(this.config.closeOnTab){
36047             ti.on("beforeclose", function(t, e){
36048                 e.cancel = true;
36049                 this.remove(panel);
36050             }, this);
36051         }
36052         
36053         panel.tabItem = ti;
36054         
36055         return ti;
36056     },
36057
36058     updatePanelTitle : function(panel, title)
36059     {
36060         if(this.activePanel == panel){
36061             this.updateTitle(title);
36062         }
36063         if(this.tabs){
36064             var ti = this.tabs.getTab(panel.getEl().id);
36065             ti.setText(title);
36066             if(panel.tabTip !== undefined){
36067                 ti.setTooltip(panel.tabTip);
36068             }
36069         }
36070     },
36071
36072     updateTitle : function(title){
36073         if(this.titleTextEl && !this.config.title){
36074             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36075         }
36076     },
36077
36078     setActivePanel : function(panel)
36079     {
36080         panel = this.getPanel(panel);
36081         if(this.activePanel && this.activePanel != panel){
36082             if(this.activePanel.setActiveState(false) === false){
36083                 return;
36084             }
36085         }
36086         this.activePanel = panel;
36087         panel.setActiveState(true);
36088         if(this.panelSize){
36089             panel.setSize(this.panelSize.width, this.panelSize.height);
36090         }
36091         if(this.closeBtn){
36092             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36093         }
36094         this.updateTitle(panel.getTitle());
36095         if(this.tabs){
36096             this.fireEvent("invalidated", this);
36097         }
36098         this.fireEvent("panelactivated", this, panel);
36099     },
36100
36101     /**
36102      * Shows the specified panel.
36103      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36104      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36105      */
36106     showPanel : function(panel)
36107     {
36108         panel = this.getPanel(panel);
36109         if(panel){
36110             if(this.tabs){
36111                 var tab = this.tabs.getTab(panel.getEl().id);
36112                 if(tab.isHidden()){
36113                     this.tabs.unhideTab(tab.id);
36114                 }
36115                 tab.activate();
36116             }else{
36117                 this.setActivePanel(panel);
36118             }
36119         }
36120         return panel;
36121     },
36122
36123     /**
36124      * Get the active panel for this region.
36125      * @return {Roo.ContentPanel} The active panel or null
36126      */
36127     getActivePanel : function(){
36128         return this.activePanel;
36129     },
36130
36131     validateVisibility : function(){
36132         if(this.panels.getCount() < 1){
36133             this.updateTitle("&#160;");
36134             this.closeBtn.hide();
36135             this.hide();
36136         }else{
36137             if(!this.isVisible()){
36138                 this.show();
36139             }
36140         }
36141     },
36142
36143     /**
36144      * Adds the passed ContentPanel(s) to this region.
36145      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36146      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36147      */
36148     add : function(panel)
36149     {
36150         if(arguments.length > 1){
36151             for(var i = 0, len = arguments.length; i < len; i++) {
36152                 this.add(arguments[i]);
36153             }
36154             return null;
36155         }
36156         
36157         // if we have not been rendered yet, then we can not really do much of this..
36158         if (!this.bodyEl) {
36159             this.unrendered_panels.push(panel);
36160             return panel;
36161         }
36162         
36163         
36164         
36165         
36166         if(this.hasPanel(panel)){
36167             this.showPanel(panel);
36168             return panel;
36169         }
36170         panel.setRegion(this);
36171         this.panels.add(panel);
36172        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36173             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36174             // and hide them... ???
36175             this.bodyEl.dom.appendChild(panel.getEl().dom);
36176             if(panel.background !== true){
36177                 this.setActivePanel(panel);
36178             }
36179             this.fireEvent("paneladded", this, panel);
36180             return panel;
36181         }
36182         */
36183         if(!this.tabs){
36184             this.initTabs();
36185         }else{
36186             this.initPanelAsTab(panel);
36187         }
36188         
36189         
36190         if(panel.background !== true){
36191             this.tabs.activate(panel.getEl().id);
36192         }
36193         this.fireEvent("paneladded", this, panel);
36194         return panel;
36195     },
36196
36197     /**
36198      * Hides the tab for the specified panel.
36199      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36200      */
36201     hidePanel : function(panel){
36202         if(this.tabs && (panel = this.getPanel(panel))){
36203             this.tabs.hideTab(panel.getEl().id);
36204         }
36205     },
36206
36207     /**
36208      * Unhides the tab for a previously hidden panel.
36209      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36210      */
36211     unhidePanel : function(panel){
36212         if(this.tabs && (panel = this.getPanel(panel))){
36213             this.tabs.unhideTab(panel.getEl().id);
36214         }
36215     },
36216
36217     clearPanels : function(){
36218         while(this.panels.getCount() > 0){
36219              this.remove(this.panels.first());
36220         }
36221     },
36222
36223     /**
36224      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36225      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36226      * @param {Boolean} preservePanel Overrides the config preservePanel option
36227      * @return {Roo.ContentPanel} The panel that was removed
36228      */
36229     remove : function(panel, preservePanel)
36230     {
36231         panel = this.getPanel(panel);
36232         if(!panel){
36233             return null;
36234         }
36235         var e = {};
36236         this.fireEvent("beforeremove", this, panel, e);
36237         if(e.cancel === true){
36238             return null;
36239         }
36240         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36241         var panelId = panel.getId();
36242         this.panels.removeKey(panelId);
36243         if(preservePanel){
36244             document.body.appendChild(panel.getEl().dom);
36245         }
36246         if(this.tabs){
36247             this.tabs.removeTab(panel.getEl().id);
36248         }else if (!preservePanel){
36249             this.bodyEl.dom.removeChild(panel.getEl().dom);
36250         }
36251         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36252             var p = this.panels.first();
36253             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36254             tempEl.appendChild(p.getEl().dom);
36255             this.bodyEl.update("");
36256             this.bodyEl.dom.appendChild(p.getEl().dom);
36257             tempEl = null;
36258             this.updateTitle(p.getTitle());
36259             this.tabs = null;
36260             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36261             this.setActivePanel(p);
36262         }
36263         panel.setRegion(null);
36264         if(this.activePanel == panel){
36265             this.activePanel = null;
36266         }
36267         if(this.config.autoDestroy !== false && preservePanel !== true){
36268             try{panel.destroy();}catch(e){}
36269         }
36270         this.fireEvent("panelremoved", this, panel);
36271         return panel;
36272     },
36273
36274     /**
36275      * Returns the TabPanel component used by this region
36276      * @return {Roo.TabPanel}
36277      */
36278     getTabs : function(){
36279         return this.tabs;
36280     },
36281
36282     createTool : function(parentEl, className){
36283         var btn = Roo.DomHelper.append(parentEl, {
36284             tag: "div",
36285             cls: "x-layout-tools-button",
36286             children: [ {
36287                 tag: "div",
36288                 cls: "roo-layout-tools-button-inner " + className,
36289                 html: "&#160;"
36290             }]
36291         }, true);
36292         btn.addClassOnOver("roo-layout-tools-button-over");
36293         return btn;
36294     }
36295 });/*
36296  * Based on:
36297  * Ext JS Library 1.1.1
36298  * Copyright(c) 2006-2007, Ext JS, LLC.
36299  *
36300  * Originally Released Under LGPL - original licence link has changed is not relivant.
36301  *
36302  * Fork - LGPL
36303  * <script type="text/javascript">
36304  */
36305  
36306
36307
36308 /**
36309  * @class Roo.SplitLayoutRegion
36310  * @extends Roo.LayoutRegion
36311  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36312  */
36313 Roo.bootstrap.layout.Split = function(config){
36314     this.cursor = config.cursor;
36315     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36316 };
36317
36318 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36319 {
36320     splitTip : "Drag to resize.",
36321     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36322     useSplitTips : false,
36323
36324     applyConfig : function(config){
36325         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36326     },
36327     
36328     onRender : function(ctr,pos) {
36329         
36330         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36331         if(!this.config.split){
36332             return;
36333         }
36334         if(!this.split){
36335             
36336             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36337                             tag: "div",
36338                             id: this.el.id + "-split",
36339                             cls: "roo-layout-split roo-layout-split-"+this.position,
36340                             html: "&#160;"
36341             });
36342             /** The SplitBar for this region 
36343             * @type Roo.SplitBar */
36344             // does not exist yet...
36345             Roo.log([this.position, this.orientation]);
36346             
36347             this.split = new Roo.bootstrap.SplitBar({
36348                 dragElement : splitEl,
36349                 resizingElement: this.el,
36350                 orientation : this.orientation
36351             });
36352             
36353             this.split.on("moved", this.onSplitMove, this);
36354             this.split.useShim = this.config.useShim === true;
36355             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36356             if(this.useSplitTips){
36357                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36358             }
36359             //if(config.collapsible){
36360             //    this.split.el.on("dblclick", this.collapse,  this);
36361             //}
36362         }
36363         if(typeof this.config.minSize != "undefined"){
36364             this.split.minSize = this.config.minSize;
36365         }
36366         if(typeof this.config.maxSize != "undefined"){
36367             this.split.maxSize = this.config.maxSize;
36368         }
36369         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36370             this.hideSplitter();
36371         }
36372         
36373     },
36374
36375     getHMaxSize : function(){
36376          var cmax = this.config.maxSize || 10000;
36377          var center = this.mgr.getRegion("center");
36378          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36379     },
36380
36381     getVMaxSize : function(){
36382          var cmax = this.config.maxSize || 10000;
36383          var center = this.mgr.getRegion("center");
36384          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36385     },
36386
36387     onSplitMove : function(split, newSize){
36388         this.fireEvent("resized", this, newSize);
36389     },
36390     
36391     /** 
36392      * Returns the {@link Roo.SplitBar} for this region.
36393      * @return {Roo.SplitBar}
36394      */
36395     getSplitBar : function(){
36396         return this.split;
36397     },
36398     
36399     hide : function(){
36400         this.hideSplitter();
36401         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36402     },
36403
36404     hideSplitter : function(){
36405         if(this.split){
36406             this.split.el.setLocation(-2000,-2000);
36407             this.split.el.hide();
36408         }
36409     },
36410
36411     show : function(){
36412         if(this.split){
36413             this.split.el.show();
36414         }
36415         Roo.bootstrap.layout.Split.superclass.show.call(this);
36416     },
36417     
36418     beforeSlide: function(){
36419         if(Roo.isGecko){// firefox overflow auto bug workaround
36420             this.bodyEl.clip();
36421             if(this.tabs) {
36422                 this.tabs.bodyEl.clip();
36423             }
36424             if(this.activePanel){
36425                 this.activePanel.getEl().clip();
36426                 
36427                 if(this.activePanel.beforeSlide){
36428                     this.activePanel.beforeSlide();
36429                 }
36430             }
36431         }
36432     },
36433     
36434     afterSlide : function(){
36435         if(Roo.isGecko){// firefox overflow auto bug workaround
36436             this.bodyEl.unclip();
36437             if(this.tabs) {
36438                 this.tabs.bodyEl.unclip();
36439             }
36440             if(this.activePanel){
36441                 this.activePanel.getEl().unclip();
36442                 if(this.activePanel.afterSlide){
36443                     this.activePanel.afterSlide();
36444                 }
36445             }
36446         }
36447     },
36448
36449     initAutoHide : function(){
36450         if(this.autoHide !== false){
36451             if(!this.autoHideHd){
36452                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36453                 this.autoHideHd = {
36454                     "mouseout": function(e){
36455                         if(!e.within(this.el, true)){
36456                             st.delay(500);
36457                         }
36458                     },
36459                     "mouseover" : function(e){
36460                         st.cancel();
36461                     },
36462                     scope : this
36463                 };
36464             }
36465             this.el.on(this.autoHideHd);
36466         }
36467     },
36468
36469     clearAutoHide : function(){
36470         if(this.autoHide !== false){
36471             this.el.un("mouseout", this.autoHideHd.mouseout);
36472             this.el.un("mouseover", this.autoHideHd.mouseover);
36473         }
36474     },
36475
36476     clearMonitor : function(){
36477         Roo.get(document).un("click", this.slideInIf, this);
36478     },
36479
36480     // these names are backwards but not changed for compat
36481     slideOut : function(){
36482         if(this.isSlid || this.el.hasActiveFx()){
36483             return;
36484         }
36485         this.isSlid = true;
36486         if(this.collapseBtn){
36487             this.collapseBtn.hide();
36488         }
36489         this.closeBtnState = this.closeBtn.getStyle('display');
36490         this.closeBtn.hide();
36491         if(this.stickBtn){
36492             this.stickBtn.show();
36493         }
36494         this.el.show();
36495         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36496         this.beforeSlide();
36497         this.el.setStyle("z-index", 10001);
36498         this.el.slideIn(this.getSlideAnchor(), {
36499             callback: function(){
36500                 this.afterSlide();
36501                 this.initAutoHide();
36502                 Roo.get(document).on("click", this.slideInIf, this);
36503                 this.fireEvent("slideshow", this);
36504             },
36505             scope: this,
36506             block: true
36507         });
36508     },
36509
36510     afterSlideIn : function(){
36511         this.clearAutoHide();
36512         this.isSlid = false;
36513         this.clearMonitor();
36514         this.el.setStyle("z-index", "");
36515         if(this.collapseBtn){
36516             this.collapseBtn.show();
36517         }
36518         this.closeBtn.setStyle('display', this.closeBtnState);
36519         if(this.stickBtn){
36520             this.stickBtn.hide();
36521         }
36522         this.fireEvent("slidehide", this);
36523     },
36524
36525     slideIn : function(cb){
36526         if(!this.isSlid || this.el.hasActiveFx()){
36527             Roo.callback(cb);
36528             return;
36529         }
36530         this.isSlid = false;
36531         this.beforeSlide();
36532         this.el.slideOut(this.getSlideAnchor(), {
36533             callback: function(){
36534                 this.el.setLeftTop(-10000, -10000);
36535                 this.afterSlide();
36536                 this.afterSlideIn();
36537                 Roo.callback(cb);
36538             },
36539             scope: this,
36540             block: true
36541         });
36542     },
36543     
36544     slideInIf : function(e){
36545         if(!e.within(this.el)){
36546             this.slideIn();
36547         }
36548     },
36549
36550     animateCollapse : function(){
36551         this.beforeSlide();
36552         this.el.setStyle("z-index", 20000);
36553         var anchor = this.getSlideAnchor();
36554         this.el.slideOut(anchor, {
36555             callback : function(){
36556                 this.el.setStyle("z-index", "");
36557                 this.collapsedEl.slideIn(anchor, {duration:.3});
36558                 this.afterSlide();
36559                 this.el.setLocation(-10000,-10000);
36560                 this.el.hide();
36561                 this.fireEvent("collapsed", this);
36562             },
36563             scope: this,
36564             block: true
36565         });
36566     },
36567
36568     animateExpand : function(){
36569         this.beforeSlide();
36570         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36571         this.el.setStyle("z-index", 20000);
36572         this.collapsedEl.hide({
36573             duration:.1
36574         });
36575         this.el.slideIn(this.getSlideAnchor(), {
36576             callback : function(){
36577                 this.el.setStyle("z-index", "");
36578                 this.afterSlide();
36579                 if(this.split){
36580                     this.split.el.show();
36581                 }
36582                 this.fireEvent("invalidated", this);
36583                 this.fireEvent("expanded", this);
36584             },
36585             scope: this,
36586             block: true
36587         });
36588     },
36589
36590     anchors : {
36591         "west" : "left",
36592         "east" : "right",
36593         "north" : "top",
36594         "south" : "bottom"
36595     },
36596
36597     sanchors : {
36598         "west" : "l",
36599         "east" : "r",
36600         "north" : "t",
36601         "south" : "b"
36602     },
36603
36604     canchors : {
36605         "west" : "tl-tr",
36606         "east" : "tr-tl",
36607         "north" : "tl-bl",
36608         "south" : "bl-tl"
36609     },
36610
36611     getAnchor : function(){
36612         return this.anchors[this.position];
36613     },
36614
36615     getCollapseAnchor : function(){
36616         return this.canchors[this.position];
36617     },
36618
36619     getSlideAnchor : function(){
36620         return this.sanchors[this.position];
36621     },
36622
36623     getAlignAdj : function(){
36624         var cm = this.cmargins;
36625         switch(this.position){
36626             case "west":
36627                 return [0, 0];
36628             break;
36629             case "east":
36630                 return [0, 0];
36631             break;
36632             case "north":
36633                 return [0, 0];
36634             break;
36635             case "south":
36636                 return [0, 0];
36637             break;
36638         }
36639     },
36640
36641     getExpandAdj : function(){
36642         var c = this.collapsedEl, cm = this.cmargins;
36643         switch(this.position){
36644             case "west":
36645                 return [-(cm.right+c.getWidth()+cm.left), 0];
36646             break;
36647             case "east":
36648                 return [cm.right+c.getWidth()+cm.left, 0];
36649             break;
36650             case "north":
36651                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36652             break;
36653             case "south":
36654                 return [0, cm.top+cm.bottom+c.getHeight()];
36655             break;
36656         }
36657     }
36658 });/*
36659  * Based on:
36660  * Ext JS Library 1.1.1
36661  * Copyright(c) 2006-2007, Ext JS, LLC.
36662  *
36663  * Originally Released Under LGPL - original licence link has changed is not relivant.
36664  *
36665  * Fork - LGPL
36666  * <script type="text/javascript">
36667  */
36668 /*
36669  * These classes are private internal classes
36670  */
36671 Roo.bootstrap.layout.Center = function(config){
36672     config.region = "center";
36673     Roo.bootstrap.layout.Region.call(this, config);
36674     this.visible = true;
36675     this.minWidth = config.minWidth || 20;
36676     this.minHeight = config.minHeight || 20;
36677 };
36678
36679 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36680     hide : function(){
36681         // center panel can't be hidden
36682     },
36683     
36684     show : function(){
36685         // center panel can't be hidden
36686     },
36687     
36688     getMinWidth: function(){
36689         return this.minWidth;
36690     },
36691     
36692     getMinHeight: function(){
36693         return this.minHeight;
36694     }
36695 });
36696
36697
36698
36699
36700  
36701
36702
36703
36704
36705
36706 Roo.bootstrap.layout.North = function(config)
36707 {
36708     config.region = 'north';
36709     config.cursor = 'n-resize';
36710     
36711     Roo.bootstrap.layout.Split.call(this, config);
36712     
36713     
36714     if(this.split){
36715         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36716         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36717         this.split.el.addClass("roo-layout-split-v");
36718     }
36719     var size = config.initialSize || config.height;
36720     if(typeof size != "undefined"){
36721         this.el.setHeight(size);
36722     }
36723 };
36724 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36725 {
36726     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36727     
36728     
36729     
36730     getBox : function(){
36731         if(this.collapsed){
36732             return this.collapsedEl.getBox();
36733         }
36734         var box = this.el.getBox();
36735         if(this.split){
36736             box.height += this.split.el.getHeight();
36737         }
36738         return box;
36739     },
36740     
36741     updateBox : function(box){
36742         if(this.split && !this.collapsed){
36743             box.height -= this.split.el.getHeight();
36744             this.split.el.setLeft(box.x);
36745             this.split.el.setTop(box.y+box.height);
36746             this.split.el.setWidth(box.width);
36747         }
36748         if(this.collapsed){
36749             this.updateBody(box.width, null);
36750         }
36751         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36752     }
36753 });
36754
36755
36756
36757
36758
36759 Roo.bootstrap.layout.South = function(config){
36760     config.region = 'south';
36761     config.cursor = 's-resize';
36762     Roo.bootstrap.layout.Split.call(this, config);
36763     if(this.split){
36764         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36765         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36766         this.split.el.addClass("roo-layout-split-v");
36767     }
36768     var size = config.initialSize || config.height;
36769     if(typeof size != "undefined"){
36770         this.el.setHeight(size);
36771     }
36772 };
36773
36774 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36775     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36776     getBox : function(){
36777         if(this.collapsed){
36778             return this.collapsedEl.getBox();
36779         }
36780         var box = this.el.getBox();
36781         if(this.split){
36782             var sh = this.split.el.getHeight();
36783             box.height += sh;
36784             box.y -= sh;
36785         }
36786         return box;
36787     },
36788     
36789     updateBox : function(box){
36790         if(this.split && !this.collapsed){
36791             var sh = this.split.el.getHeight();
36792             box.height -= sh;
36793             box.y += sh;
36794             this.split.el.setLeft(box.x);
36795             this.split.el.setTop(box.y-sh);
36796             this.split.el.setWidth(box.width);
36797         }
36798         if(this.collapsed){
36799             this.updateBody(box.width, null);
36800         }
36801         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36802     }
36803 });
36804
36805 Roo.bootstrap.layout.East = function(config){
36806     config.region = "east";
36807     config.cursor = "e-resize";
36808     Roo.bootstrap.layout.Split.call(this, config);
36809     if(this.split){
36810         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36811         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36812         this.split.el.addClass("roo-layout-split-h");
36813     }
36814     var size = config.initialSize || config.width;
36815     if(typeof size != "undefined"){
36816         this.el.setWidth(size);
36817     }
36818 };
36819 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36820     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36821     getBox : function(){
36822         if(this.collapsed){
36823             return this.collapsedEl.getBox();
36824         }
36825         var box = this.el.getBox();
36826         if(this.split){
36827             var sw = this.split.el.getWidth();
36828             box.width += sw;
36829             box.x -= sw;
36830         }
36831         return box;
36832     },
36833
36834     updateBox : function(box){
36835         if(this.split && !this.collapsed){
36836             var sw = this.split.el.getWidth();
36837             box.width -= sw;
36838             this.split.el.setLeft(box.x);
36839             this.split.el.setTop(box.y);
36840             this.split.el.setHeight(box.height);
36841             box.x += sw;
36842         }
36843         if(this.collapsed){
36844             this.updateBody(null, box.height);
36845         }
36846         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36847     }
36848 });
36849
36850 Roo.bootstrap.layout.West = function(config){
36851     config.region = "west";
36852     config.cursor = "w-resize";
36853     
36854     Roo.bootstrap.layout.Split.call(this, config);
36855     if(this.split){
36856         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36857         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36858         this.split.el.addClass("roo-layout-split-h");
36859     }
36860     
36861 };
36862 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36863     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36864     
36865     onRender: function(ctr, pos)
36866     {
36867         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36868         var size = this.config.initialSize || this.config.width;
36869         if(typeof size != "undefined"){
36870             this.el.setWidth(size);
36871         }
36872     },
36873     
36874     getBox : function(){
36875         if(this.collapsed){
36876             return this.collapsedEl.getBox();
36877         }
36878         var box = this.el.getBox();
36879         if(this.split){
36880             box.width += this.split.el.getWidth();
36881         }
36882         return box;
36883     },
36884     
36885     updateBox : function(box){
36886         if(this.split && !this.collapsed){
36887             var sw = this.split.el.getWidth();
36888             box.width -= sw;
36889             this.split.el.setLeft(box.x+box.width);
36890             this.split.el.setTop(box.y);
36891             this.split.el.setHeight(box.height);
36892         }
36893         if(this.collapsed){
36894             this.updateBody(null, box.height);
36895         }
36896         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36897     }
36898 });
36899 Roo.namespace("Roo.bootstrap.panel");/*
36900  * Based on:
36901  * Ext JS Library 1.1.1
36902  * Copyright(c) 2006-2007, Ext JS, LLC.
36903  *
36904  * Originally Released Under LGPL - original licence link has changed is not relivant.
36905  *
36906  * Fork - LGPL
36907  * <script type="text/javascript">
36908  */
36909 /**
36910  * @class Roo.ContentPanel
36911  * @extends Roo.util.Observable
36912  * A basic ContentPanel element.
36913  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36914  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36915  * @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
36916  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36917  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36918  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36919  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36920  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36921  * @cfg {String} title          The title for this panel
36922  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36923  * @cfg {String} url            Calls {@link #setUrl} with this value
36924  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36925  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36926  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36927  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36928  * @cfg {Boolean} badges render the badges
36929
36930  * @constructor
36931  * Create a new ContentPanel.
36932  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36933  * @param {String/Object} config A string to set only the title or a config object
36934  * @param {String} content (optional) Set the HTML content for this panel
36935  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36936  */
36937 Roo.bootstrap.panel.Content = function( config){
36938     
36939     this.tpl = config.tpl || false;
36940     
36941     var el = config.el;
36942     var content = config.content;
36943
36944     if(config.autoCreate){ // xtype is available if this is called from factory
36945         el = Roo.id();
36946     }
36947     this.el = Roo.get(el);
36948     if(!this.el && config && config.autoCreate){
36949         if(typeof config.autoCreate == "object"){
36950             if(!config.autoCreate.id){
36951                 config.autoCreate.id = config.id||el;
36952             }
36953             this.el = Roo.DomHelper.append(document.body,
36954                         config.autoCreate, true);
36955         }else{
36956             var elcfg =  {   tag: "div",
36957                             cls: "roo-layout-inactive-content",
36958                             id: config.id||el
36959                             };
36960             if (config.html) {
36961                 elcfg.html = config.html;
36962                 
36963             }
36964                         
36965             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36966         }
36967     } 
36968     this.closable = false;
36969     this.loaded = false;
36970     this.active = false;
36971    
36972       
36973     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36974         
36975         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36976         
36977         this.wrapEl = this.el; //this.el.wrap();
36978         var ti = [];
36979         if (config.toolbar.items) {
36980             ti = config.toolbar.items ;
36981             delete config.toolbar.items ;
36982         }
36983         
36984         var nitems = [];
36985         this.toolbar.render(this.wrapEl, 'before');
36986         for(var i =0;i < ti.length;i++) {
36987           //  Roo.log(['add child', items[i]]);
36988             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36989         }
36990         this.toolbar.items = nitems;
36991         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36992         delete config.toolbar;
36993         
36994     }
36995     /*
36996     // xtype created footer. - not sure if will work as we normally have to render first..
36997     if (this.footer && !this.footer.el && this.footer.xtype) {
36998         if (!this.wrapEl) {
36999             this.wrapEl = this.el.wrap();
37000         }
37001     
37002         this.footer.container = this.wrapEl.createChild();
37003          
37004         this.footer = Roo.factory(this.footer, Roo);
37005         
37006     }
37007     */
37008     
37009      if(typeof config == "string"){
37010         this.title = config;
37011     }else{
37012         Roo.apply(this, config);
37013     }
37014     
37015     if(this.resizeEl){
37016         this.resizeEl = Roo.get(this.resizeEl, true);
37017     }else{
37018         this.resizeEl = this.el;
37019     }
37020     // handle view.xtype
37021     
37022  
37023     
37024     
37025     this.addEvents({
37026         /**
37027          * @event activate
37028          * Fires when this panel is activated. 
37029          * @param {Roo.ContentPanel} this
37030          */
37031         "activate" : true,
37032         /**
37033          * @event deactivate
37034          * Fires when this panel is activated. 
37035          * @param {Roo.ContentPanel} this
37036          */
37037         "deactivate" : true,
37038
37039         /**
37040          * @event resize
37041          * Fires when this panel is resized if fitToFrame is true.
37042          * @param {Roo.ContentPanel} this
37043          * @param {Number} width The width after any component adjustments
37044          * @param {Number} height The height after any component adjustments
37045          */
37046         "resize" : true,
37047         
37048          /**
37049          * @event render
37050          * Fires when this tab is created
37051          * @param {Roo.ContentPanel} this
37052          */
37053         "render" : true
37054         
37055         
37056         
37057     });
37058     
37059
37060     
37061     
37062     if(this.autoScroll){
37063         this.resizeEl.setStyle("overflow", "auto");
37064     } else {
37065         // fix randome scrolling
37066         //this.el.on('scroll', function() {
37067         //    Roo.log('fix random scolling');
37068         //    this.scrollTo('top',0); 
37069         //});
37070     }
37071     content = content || this.content;
37072     if(content){
37073         this.setContent(content);
37074     }
37075     if(config && config.url){
37076         this.setUrl(this.url, this.params, this.loadOnce);
37077     }
37078     
37079     
37080     
37081     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37082     
37083     if (this.view && typeof(this.view.xtype) != 'undefined') {
37084         this.view.el = this.el.appendChild(document.createElement("div"));
37085         this.view = Roo.factory(this.view); 
37086         this.view.render  &&  this.view.render(false, '');  
37087     }
37088     
37089     
37090     this.fireEvent('render', this);
37091 };
37092
37093 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37094     
37095     tabTip : '',
37096     
37097     setRegion : function(region){
37098         this.region = region;
37099         this.setActiveClass(region && !this.background);
37100     },
37101     
37102     
37103     setActiveClass: function(state)
37104     {
37105         if(state){
37106            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37107            this.el.setStyle('position','relative');
37108         }else{
37109            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37110            this.el.setStyle('position', 'absolute');
37111         } 
37112     },
37113     
37114     /**
37115      * Returns the toolbar for this Panel if one was configured. 
37116      * @return {Roo.Toolbar} 
37117      */
37118     getToolbar : function(){
37119         return this.toolbar;
37120     },
37121     
37122     setActiveState : function(active)
37123     {
37124         this.active = active;
37125         this.setActiveClass(active);
37126         if(!active){
37127             if(this.fireEvent("deactivate", this) === false){
37128                 return false;
37129             }
37130             return true;
37131         }
37132         this.fireEvent("activate", this);
37133         return true;
37134     },
37135     /**
37136      * Updates this panel's element
37137      * @param {String} content The new content
37138      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37139     */
37140     setContent : function(content, loadScripts){
37141         this.el.update(content, loadScripts);
37142     },
37143
37144     ignoreResize : function(w, h){
37145         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37146             return true;
37147         }else{
37148             this.lastSize = {width: w, height: h};
37149             return false;
37150         }
37151     },
37152     /**
37153      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37154      * @return {Roo.UpdateManager} The UpdateManager
37155      */
37156     getUpdateManager : function(){
37157         return this.el.getUpdateManager();
37158     },
37159      /**
37160      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37161      * @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:
37162 <pre><code>
37163 panel.load({
37164     url: "your-url.php",
37165     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37166     callback: yourFunction,
37167     scope: yourObject, //(optional scope)
37168     discardUrl: false,
37169     nocache: false,
37170     text: "Loading...",
37171     timeout: 30,
37172     scripts: false
37173 });
37174 </code></pre>
37175      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37176      * 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.
37177      * @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}
37178      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37179      * @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.
37180      * @return {Roo.ContentPanel} this
37181      */
37182     load : function(){
37183         var um = this.el.getUpdateManager();
37184         um.update.apply(um, arguments);
37185         return this;
37186     },
37187
37188
37189     /**
37190      * 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.
37191      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37192      * @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)
37193      * @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)
37194      * @return {Roo.UpdateManager} The UpdateManager
37195      */
37196     setUrl : function(url, params, loadOnce){
37197         if(this.refreshDelegate){
37198             this.removeListener("activate", this.refreshDelegate);
37199         }
37200         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37201         this.on("activate", this.refreshDelegate);
37202         return this.el.getUpdateManager();
37203     },
37204     
37205     _handleRefresh : function(url, params, loadOnce){
37206         if(!loadOnce || !this.loaded){
37207             var updater = this.el.getUpdateManager();
37208             updater.update(url, params, this._setLoaded.createDelegate(this));
37209         }
37210     },
37211     
37212     _setLoaded : function(){
37213         this.loaded = true;
37214     }, 
37215     
37216     /**
37217      * Returns this panel's id
37218      * @return {String} 
37219      */
37220     getId : function(){
37221         return this.el.id;
37222     },
37223     
37224     /** 
37225      * Returns this panel's element - used by regiosn to add.
37226      * @return {Roo.Element} 
37227      */
37228     getEl : function(){
37229         return this.wrapEl || this.el;
37230     },
37231     
37232    
37233     
37234     adjustForComponents : function(width, height)
37235     {
37236         //Roo.log('adjustForComponents ');
37237         if(this.resizeEl != this.el){
37238             width -= this.el.getFrameWidth('lr');
37239             height -= this.el.getFrameWidth('tb');
37240         }
37241         if(this.toolbar){
37242             var te = this.toolbar.getEl();
37243             te.setWidth(width);
37244             height -= te.getHeight();
37245         }
37246         if(this.footer){
37247             var te = this.footer.getEl();
37248             te.setWidth(width);
37249             height -= te.getHeight();
37250         }
37251         
37252         
37253         if(this.adjustments){
37254             width += this.adjustments[0];
37255             height += this.adjustments[1];
37256         }
37257         return {"width": width, "height": height};
37258     },
37259     
37260     setSize : function(width, height){
37261         if(this.fitToFrame && !this.ignoreResize(width, height)){
37262             if(this.fitContainer && this.resizeEl != this.el){
37263                 this.el.setSize(width, height);
37264             }
37265             var size = this.adjustForComponents(width, height);
37266             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37267             this.fireEvent('resize', this, size.width, size.height);
37268         }
37269     },
37270     
37271     /**
37272      * Returns this panel's title
37273      * @return {String} 
37274      */
37275     getTitle : function(){
37276         
37277         if (typeof(this.title) != 'object') {
37278             return this.title;
37279         }
37280         
37281         var t = '';
37282         for (var k in this.title) {
37283             if (!this.title.hasOwnProperty(k)) {
37284                 continue;
37285             }
37286             
37287             if (k.indexOf('-') >= 0) {
37288                 var s = k.split('-');
37289                 for (var i = 0; i<s.length; i++) {
37290                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37291                 }
37292             } else {
37293                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37294             }
37295         }
37296         return t;
37297     },
37298     
37299     /**
37300      * Set this panel's title
37301      * @param {String} title
37302      */
37303     setTitle : function(title){
37304         this.title = title;
37305         if(this.region){
37306             this.region.updatePanelTitle(this, title);
37307         }
37308     },
37309     
37310     /**
37311      * Returns true is this panel was configured to be closable
37312      * @return {Boolean} 
37313      */
37314     isClosable : function(){
37315         return this.closable;
37316     },
37317     
37318     beforeSlide : function(){
37319         this.el.clip();
37320         this.resizeEl.clip();
37321     },
37322     
37323     afterSlide : function(){
37324         this.el.unclip();
37325         this.resizeEl.unclip();
37326     },
37327     
37328     /**
37329      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37330      *   Will fail silently if the {@link #setUrl} method has not been called.
37331      *   This does not activate the panel, just updates its content.
37332      */
37333     refresh : function(){
37334         if(this.refreshDelegate){
37335            this.loaded = false;
37336            this.refreshDelegate();
37337         }
37338     },
37339     
37340     /**
37341      * Destroys this panel
37342      */
37343     destroy : function(){
37344         this.el.removeAllListeners();
37345         var tempEl = document.createElement("span");
37346         tempEl.appendChild(this.el.dom);
37347         tempEl.innerHTML = "";
37348         this.el.remove();
37349         this.el = null;
37350     },
37351     
37352     /**
37353      * form - if the content panel contains a form - this is a reference to it.
37354      * @type {Roo.form.Form}
37355      */
37356     form : false,
37357     /**
37358      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37359      *    This contains a reference to it.
37360      * @type {Roo.View}
37361      */
37362     view : false,
37363     
37364       /**
37365      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37366      * <pre><code>
37367
37368 layout.addxtype({
37369        xtype : 'Form',
37370        items: [ .... ]
37371    }
37372 );
37373
37374 </code></pre>
37375      * @param {Object} cfg Xtype definition of item to add.
37376      */
37377     
37378     
37379     getChildContainer: function () {
37380         return this.getEl();
37381     }
37382     
37383     
37384     /*
37385         var  ret = new Roo.factory(cfg);
37386         return ret;
37387         
37388         
37389         // add form..
37390         if (cfg.xtype.match(/^Form$/)) {
37391             
37392             var el;
37393             //if (this.footer) {
37394             //    el = this.footer.container.insertSibling(false, 'before');
37395             //} else {
37396                 el = this.el.createChild();
37397             //}
37398
37399             this.form = new  Roo.form.Form(cfg);
37400             
37401             
37402             if ( this.form.allItems.length) {
37403                 this.form.render(el.dom);
37404             }
37405             return this.form;
37406         }
37407         // should only have one of theses..
37408         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37409             // views.. should not be just added - used named prop 'view''
37410             
37411             cfg.el = this.el.appendChild(document.createElement("div"));
37412             // factory?
37413             
37414             var ret = new Roo.factory(cfg);
37415              
37416              ret.render && ret.render(false, ''); // render blank..
37417             this.view = ret;
37418             return ret;
37419         }
37420         return false;
37421     }
37422     \*/
37423 });
37424  
37425 /**
37426  * @class Roo.bootstrap.panel.Grid
37427  * @extends Roo.bootstrap.panel.Content
37428  * @constructor
37429  * Create a new GridPanel.
37430  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37431  * @param {Object} config A the config object
37432   
37433  */
37434
37435
37436
37437 Roo.bootstrap.panel.Grid = function(config)
37438 {
37439     
37440       
37441     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37442         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37443
37444     config.el = this.wrapper;
37445     //this.el = this.wrapper;
37446     
37447       if (config.container) {
37448         // ctor'ed from a Border/panel.grid
37449         
37450         
37451         this.wrapper.setStyle("overflow", "hidden");
37452         this.wrapper.addClass('roo-grid-container');
37453
37454     }
37455     
37456     
37457     if(config.toolbar){
37458         var tool_el = this.wrapper.createChild();    
37459         this.toolbar = Roo.factory(config.toolbar);
37460         var ti = [];
37461         if (config.toolbar.items) {
37462             ti = config.toolbar.items ;
37463             delete config.toolbar.items ;
37464         }
37465         
37466         var nitems = [];
37467         this.toolbar.render(tool_el);
37468         for(var i =0;i < ti.length;i++) {
37469           //  Roo.log(['add child', items[i]]);
37470             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37471         }
37472         this.toolbar.items = nitems;
37473         
37474         delete config.toolbar;
37475     }
37476     
37477     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37478     config.grid.scrollBody = true;;
37479     config.grid.monitorWindowResize = false; // turn off autosizing
37480     config.grid.autoHeight = false;
37481     config.grid.autoWidth = false;
37482     
37483     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37484     
37485     if (config.background) {
37486         // render grid on panel activation (if panel background)
37487         this.on('activate', function(gp) {
37488             if (!gp.grid.rendered) {
37489                 gp.grid.render(this.wrapper);
37490                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37491             }
37492         });
37493             
37494     } else {
37495         this.grid.render(this.wrapper);
37496         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37497
37498     }
37499     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37500     // ??? needed ??? config.el = this.wrapper;
37501     
37502     
37503     
37504   
37505     // xtype created footer. - not sure if will work as we normally have to render first..
37506     if (this.footer && !this.footer.el && this.footer.xtype) {
37507         
37508         var ctr = this.grid.getView().getFooterPanel(true);
37509         this.footer.dataSource = this.grid.dataSource;
37510         this.footer = Roo.factory(this.footer, Roo);
37511         this.footer.render(ctr);
37512         
37513     }
37514     
37515     
37516     
37517     
37518      
37519 };
37520
37521 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37522     getId : function(){
37523         return this.grid.id;
37524     },
37525     
37526     /**
37527      * Returns the grid for this panel
37528      * @return {Roo.bootstrap.Table} 
37529      */
37530     getGrid : function(){
37531         return this.grid;    
37532     },
37533     
37534     setSize : function(width, height){
37535         if(!this.ignoreResize(width, height)){
37536             var grid = this.grid;
37537             var size = this.adjustForComponents(width, height);
37538             var gridel = grid.getGridEl();
37539             gridel.setSize(size.width, size.height);
37540             /*
37541             var thd = grid.getGridEl().select('thead',true).first();
37542             var tbd = grid.getGridEl().select('tbody', true).first();
37543             if (tbd) {
37544                 tbd.setSize(width, height - thd.getHeight());
37545             }
37546             */
37547             grid.autoSize();
37548         }
37549     },
37550      
37551     
37552     
37553     beforeSlide : function(){
37554         this.grid.getView().scroller.clip();
37555     },
37556     
37557     afterSlide : function(){
37558         this.grid.getView().scroller.unclip();
37559     },
37560     
37561     destroy : function(){
37562         this.grid.destroy();
37563         delete this.grid;
37564         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37565     }
37566 });
37567
37568 /**
37569  * @class Roo.bootstrap.panel.Nest
37570  * @extends Roo.bootstrap.panel.Content
37571  * @constructor
37572  * Create a new Panel, that can contain a layout.Border.
37573  * 
37574  * 
37575  * @param {Roo.BorderLayout} layout The layout for this panel
37576  * @param {String/Object} config A string to set only the title or a config object
37577  */
37578 Roo.bootstrap.panel.Nest = function(config)
37579 {
37580     // construct with only one argument..
37581     /* FIXME - implement nicer consturctors
37582     if (layout.layout) {
37583         config = layout;
37584         layout = config.layout;
37585         delete config.layout;
37586     }
37587     if (layout.xtype && !layout.getEl) {
37588         // then layout needs constructing..
37589         layout = Roo.factory(layout, Roo);
37590     }
37591     */
37592     
37593     config.el =  config.layout.getEl();
37594     
37595     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37596     
37597     config.layout.monitorWindowResize = false; // turn off autosizing
37598     this.layout = config.layout;
37599     this.layout.getEl().addClass("roo-layout-nested-layout");
37600     
37601     
37602     
37603     
37604 };
37605
37606 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37607
37608     setSize : function(width, height){
37609         if(!this.ignoreResize(width, height)){
37610             var size = this.adjustForComponents(width, height);
37611             var el = this.layout.getEl();
37612             if (size.height < 1) {
37613                 el.setWidth(size.width);   
37614             } else {
37615                 el.setSize(size.width, size.height);
37616             }
37617             var touch = el.dom.offsetWidth;
37618             this.layout.layout();
37619             // ie requires a double layout on the first pass
37620             if(Roo.isIE && !this.initialized){
37621                 this.initialized = true;
37622                 this.layout.layout();
37623             }
37624         }
37625     },
37626     
37627     // activate all subpanels if not currently active..
37628     
37629     setActiveState : function(active){
37630         this.active = active;
37631         this.setActiveClass(active);
37632         
37633         if(!active){
37634             this.fireEvent("deactivate", this);
37635             return;
37636         }
37637         
37638         this.fireEvent("activate", this);
37639         // not sure if this should happen before or after..
37640         if (!this.layout) {
37641             return; // should not happen..
37642         }
37643         var reg = false;
37644         for (var r in this.layout.regions) {
37645             reg = this.layout.getRegion(r);
37646             if (reg.getActivePanel()) {
37647                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37648                 reg.setActivePanel(reg.getActivePanel());
37649                 continue;
37650             }
37651             if (!reg.panels.length) {
37652                 continue;
37653             }
37654             reg.showPanel(reg.getPanel(0));
37655         }
37656         
37657         
37658         
37659         
37660     },
37661     
37662     /**
37663      * Returns the nested BorderLayout for this panel
37664      * @return {Roo.BorderLayout} 
37665      */
37666     getLayout : function(){
37667         return this.layout;
37668     },
37669     
37670      /**
37671      * Adds a xtype elements to the layout of the nested panel
37672      * <pre><code>
37673
37674 panel.addxtype({
37675        xtype : 'ContentPanel',
37676        region: 'west',
37677        items: [ .... ]
37678    }
37679 );
37680
37681 panel.addxtype({
37682         xtype : 'NestedLayoutPanel',
37683         region: 'west',
37684         layout: {
37685            center: { },
37686            west: { }   
37687         },
37688         items : [ ... list of content panels or nested layout panels.. ]
37689    }
37690 );
37691 </code></pre>
37692      * @param {Object} cfg Xtype definition of item to add.
37693      */
37694     addxtype : function(cfg) {
37695         return this.layout.addxtype(cfg);
37696     
37697     }
37698 });        /*
37699  * Based on:
37700  * Ext JS Library 1.1.1
37701  * Copyright(c) 2006-2007, Ext JS, LLC.
37702  *
37703  * Originally Released Under LGPL - original licence link has changed is not relivant.
37704  *
37705  * Fork - LGPL
37706  * <script type="text/javascript">
37707  */
37708 /**
37709  * @class Roo.TabPanel
37710  * @extends Roo.util.Observable
37711  * A lightweight tab container.
37712  * <br><br>
37713  * Usage:
37714  * <pre><code>
37715 // basic tabs 1, built from existing content
37716 var tabs = new Roo.TabPanel("tabs1");
37717 tabs.addTab("script", "View Script");
37718 tabs.addTab("markup", "View Markup");
37719 tabs.activate("script");
37720
37721 // more advanced tabs, built from javascript
37722 var jtabs = new Roo.TabPanel("jtabs");
37723 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37724
37725 // set up the UpdateManager
37726 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37727 var updater = tab2.getUpdateManager();
37728 updater.setDefaultUrl("ajax1.htm");
37729 tab2.on('activate', updater.refresh, updater, true);
37730
37731 // Use setUrl for Ajax loading
37732 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37733 tab3.setUrl("ajax2.htm", null, true);
37734
37735 // Disabled tab
37736 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37737 tab4.disable();
37738
37739 jtabs.activate("jtabs-1");
37740  * </code></pre>
37741  * @constructor
37742  * Create a new TabPanel.
37743  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37744  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37745  */
37746 Roo.bootstrap.panel.Tabs = function(config){
37747     /**
37748     * The container element for this TabPanel.
37749     * @type Roo.Element
37750     */
37751     this.el = Roo.get(config.el);
37752     delete config.el;
37753     if(config){
37754         if(typeof config == "boolean"){
37755             this.tabPosition = config ? "bottom" : "top";
37756         }else{
37757             Roo.apply(this, config);
37758         }
37759     }
37760     
37761     if(this.tabPosition == "bottom"){
37762         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37763         this.el.addClass("roo-tabs-bottom");
37764     }
37765     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37766     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37767     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37768     if(Roo.isIE){
37769         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37770     }
37771     if(this.tabPosition != "bottom"){
37772         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37773          * @type Roo.Element
37774          */
37775         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37776         this.el.addClass("roo-tabs-top");
37777     }
37778     this.items = [];
37779
37780     this.bodyEl.setStyle("position", "relative");
37781
37782     this.active = null;
37783     this.activateDelegate = this.activate.createDelegate(this);
37784
37785     this.addEvents({
37786         /**
37787          * @event tabchange
37788          * Fires when the active tab changes
37789          * @param {Roo.TabPanel} this
37790          * @param {Roo.TabPanelItem} activePanel The new active tab
37791          */
37792         "tabchange": true,
37793         /**
37794          * @event beforetabchange
37795          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37796          * @param {Roo.TabPanel} this
37797          * @param {Object} e Set cancel to true on this object to cancel the tab change
37798          * @param {Roo.TabPanelItem} tab The tab being changed to
37799          */
37800         "beforetabchange" : true
37801     });
37802
37803     Roo.EventManager.onWindowResize(this.onResize, this);
37804     this.cpad = this.el.getPadding("lr");
37805     this.hiddenCount = 0;
37806
37807
37808     // toolbar on the tabbar support...
37809     if (this.toolbar) {
37810         alert("no toolbar support yet");
37811         this.toolbar  = false;
37812         /*
37813         var tcfg = this.toolbar;
37814         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37815         this.toolbar = new Roo.Toolbar(tcfg);
37816         if (Roo.isSafari) {
37817             var tbl = tcfg.container.child('table', true);
37818             tbl.setAttribute('width', '100%');
37819         }
37820         */
37821         
37822     }
37823    
37824
37825
37826     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37827 };
37828
37829 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37830     /*
37831      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37832      */
37833     tabPosition : "top",
37834     /*
37835      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37836      */
37837     currentTabWidth : 0,
37838     /*
37839      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37840      */
37841     minTabWidth : 40,
37842     /*
37843      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37844      */
37845     maxTabWidth : 250,
37846     /*
37847      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37848      */
37849     preferredTabWidth : 175,
37850     /*
37851      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37852      */
37853     resizeTabs : false,
37854     /*
37855      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37856      */
37857     monitorResize : true,
37858     /*
37859      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37860      */
37861     toolbar : false,
37862
37863     /**
37864      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37865      * @param {String} id The id of the div to use <b>or create</b>
37866      * @param {String} text The text for the tab
37867      * @param {String} content (optional) Content to put in the TabPanelItem body
37868      * @param {Boolean} closable (optional) True to create a close icon on the tab
37869      * @return {Roo.TabPanelItem} The created TabPanelItem
37870      */
37871     addTab : function(id, text, content, closable, tpl)
37872     {
37873         var item = new Roo.bootstrap.panel.TabItem({
37874             panel: this,
37875             id : id,
37876             text : text,
37877             closable : closable,
37878             tpl : tpl
37879         });
37880         this.addTabItem(item);
37881         if(content){
37882             item.setContent(content);
37883         }
37884         return item;
37885     },
37886
37887     /**
37888      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37889      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37890      * @return {Roo.TabPanelItem}
37891      */
37892     getTab : function(id){
37893         return this.items[id];
37894     },
37895
37896     /**
37897      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37898      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37899      */
37900     hideTab : function(id){
37901         var t = this.items[id];
37902         if(!t.isHidden()){
37903            t.setHidden(true);
37904            this.hiddenCount++;
37905            this.autoSizeTabs();
37906         }
37907     },
37908
37909     /**
37910      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37911      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37912      */
37913     unhideTab : function(id){
37914         var t = this.items[id];
37915         if(t.isHidden()){
37916            t.setHidden(false);
37917            this.hiddenCount--;
37918            this.autoSizeTabs();
37919         }
37920     },
37921
37922     /**
37923      * Adds an existing {@link Roo.TabPanelItem}.
37924      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37925      */
37926     addTabItem : function(item){
37927         this.items[item.id] = item;
37928         this.items.push(item);
37929       //  if(this.resizeTabs){
37930     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37931   //         this.autoSizeTabs();
37932 //        }else{
37933 //            item.autoSize();
37934        // }
37935     },
37936
37937     /**
37938      * Removes a {@link Roo.TabPanelItem}.
37939      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37940      */
37941     removeTab : function(id){
37942         var items = this.items;
37943         var tab = items[id];
37944         if(!tab) { return; }
37945         var index = items.indexOf(tab);
37946         if(this.active == tab && items.length > 1){
37947             var newTab = this.getNextAvailable(index);
37948             if(newTab) {
37949                 newTab.activate();
37950             }
37951         }
37952         this.stripEl.dom.removeChild(tab.pnode.dom);
37953         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37954             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37955         }
37956         items.splice(index, 1);
37957         delete this.items[tab.id];
37958         tab.fireEvent("close", tab);
37959         tab.purgeListeners();
37960         this.autoSizeTabs();
37961     },
37962
37963     getNextAvailable : function(start){
37964         var items = this.items;
37965         var index = start;
37966         // look for a next tab that will slide over to
37967         // replace the one being removed
37968         while(index < items.length){
37969             var item = items[++index];
37970             if(item && !item.isHidden()){
37971                 return item;
37972             }
37973         }
37974         // if one isn't found select the previous tab (on the left)
37975         index = start;
37976         while(index >= 0){
37977             var item = items[--index];
37978             if(item && !item.isHidden()){
37979                 return item;
37980             }
37981         }
37982         return null;
37983     },
37984
37985     /**
37986      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37987      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37988      */
37989     disableTab : function(id){
37990         var tab = this.items[id];
37991         if(tab && this.active != tab){
37992             tab.disable();
37993         }
37994     },
37995
37996     /**
37997      * Enables a {@link Roo.TabPanelItem} that is disabled.
37998      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37999      */
38000     enableTab : function(id){
38001         var tab = this.items[id];
38002         tab.enable();
38003     },
38004
38005     /**
38006      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38007      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38008      * @return {Roo.TabPanelItem} The TabPanelItem.
38009      */
38010     activate : function(id){
38011         var tab = this.items[id];
38012         if(!tab){
38013             return null;
38014         }
38015         if(tab == this.active || tab.disabled){
38016             return tab;
38017         }
38018         var e = {};
38019         this.fireEvent("beforetabchange", this, e, tab);
38020         if(e.cancel !== true && !tab.disabled){
38021             if(this.active){
38022                 this.active.hide();
38023             }
38024             this.active = this.items[id];
38025             this.active.show();
38026             this.fireEvent("tabchange", this, this.active);
38027         }
38028         return tab;
38029     },
38030
38031     /**
38032      * Gets the active {@link Roo.TabPanelItem}.
38033      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38034      */
38035     getActiveTab : function(){
38036         return this.active;
38037     },
38038
38039     /**
38040      * Updates the tab body element to fit the height of the container element
38041      * for overflow scrolling
38042      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38043      */
38044     syncHeight : function(targetHeight){
38045         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38046         var bm = this.bodyEl.getMargins();
38047         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38048         this.bodyEl.setHeight(newHeight);
38049         return newHeight;
38050     },
38051
38052     onResize : function(){
38053         if(this.monitorResize){
38054             this.autoSizeTabs();
38055         }
38056     },
38057
38058     /**
38059      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38060      */
38061     beginUpdate : function(){
38062         this.updating = true;
38063     },
38064
38065     /**
38066      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38067      */
38068     endUpdate : function(){
38069         this.updating = false;
38070         this.autoSizeTabs();
38071     },
38072
38073     /**
38074      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38075      */
38076     autoSizeTabs : function(){
38077         var count = this.items.length;
38078         var vcount = count - this.hiddenCount;
38079         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38080             return;
38081         }
38082         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38083         var availWidth = Math.floor(w / vcount);
38084         var b = this.stripBody;
38085         if(b.getWidth() > w){
38086             var tabs = this.items;
38087             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38088             if(availWidth < this.minTabWidth){
38089                 /*if(!this.sleft){    // incomplete scrolling code
38090                     this.createScrollButtons();
38091                 }
38092                 this.showScroll();
38093                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38094             }
38095         }else{
38096             if(this.currentTabWidth < this.preferredTabWidth){
38097                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38098             }
38099         }
38100     },
38101
38102     /**
38103      * Returns the number of tabs in this TabPanel.
38104      * @return {Number}
38105      */
38106      getCount : function(){
38107          return this.items.length;
38108      },
38109
38110     /**
38111      * Resizes all the tabs to the passed width
38112      * @param {Number} The new width
38113      */
38114     setTabWidth : function(width){
38115         this.currentTabWidth = width;
38116         for(var i = 0, len = this.items.length; i < len; i++) {
38117                 if(!this.items[i].isHidden()) {
38118                 this.items[i].setWidth(width);
38119             }
38120         }
38121     },
38122
38123     /**
38124      * Destroys this TabPanel
38125      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38126      */
38127     destroy : function(removeEl){
38128         Roo.EventManager.removeResizeListener(this.onResize, this);
38129         for(var i = 0, len = this.items.length; i < len; i++){
38130             this.items[i].purgeListeners();
38131         }
38132         if(removeEl === true){
38133             this.el.update("");
38134             this.el.remove();
38135         }
38136     },
38137     
38138     createStrip : function(container)
38139     {
38140         var strip = document.createElement("nav");
38141         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38142         container.appendChild(strip);
38143         return strip;
38144     },
38145     
38146     createStripList : function(strip)
38147     {
38148         // div wrapper for retard IE
38149         // returns the "tr" element.
38150         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38151         //'<div class="x-tabs-strip-wrap">'+
38152           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38153           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38154         return strip.firstChild; //.firstChild.firstChild.firstChild;
38155     },
38156     createBody : function(container)
38157     {
38158         var body = document.createElement("div");
38159         Roo.id(body, "tab-body");
38160         //Roo.fly(body).addClass("x-tabs-body");
38161         Roo.fly(body).addClass("tab-content");
38162         container.appendChild(body);
38163         return body;
38164     },
38165     createItemBody :function(bodyEl, id){
38166         var body = Roo.getDom(id);
38167         if(!body){
38168             body = document.createElement("div");
38169             body.id = id;
38170         }
38171         //Roo.fly(body).addClass("x-tabs-item-body");
38172         Roo.fly(body).addClass("tab-pane");
38173          bodyEl.insertBefore(body, bodyEl.firstChild);
38174         return body;
38175     },
38176     /** @private */
38177     createStripElements :  function(stripEl, text, closable, tpl)
38178     {
38179         var td = document.createElement("li"); // was td..
38180         
38181         
38182         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38183         
38184         
38185         stripEl.appendChild(td);
38186         /*if(closable){
38187             td.className = "x-tabs-closable";
38188             if(!this.closeTpl){
38189                 this.closeTpl = new Roo.Template(
38190                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38191                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38192                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38193                 );
38194             }
38195             var el = this.closeTpl.overwrite(td, {"text": text});
38196             var close = el.getElementsByTagName("div")[0];
38197             var inner = el.getElementsByTagName("em")[0];
38198             return {"el": el, "close": close, "inner": inner};
38199         } else {
38200         */
38201         // not sure what this is..
38202 //            if(!this.tabTpl){
38203                 //this.tabTpl = new Roo.Template(
38204                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38205                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38206                 //);
38207 //                this.tabTpl = new Roo.Template(
38208 //                   '<a href="#">' +
38209 //                   '<span unselectable="on"' +
38210 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38211 //                            ' >{text}</span></a>'
38212 //                );
38213 //                
38214 //            }
38215
38216
38217             var template = tpl || this.tabTpl || false;
38218             
38219             if(!template){
38220                 
38221                 template = new Roo.Template(
38222                    '<a href="#">' +
38223                    '<span unselectable="on"' +
38224                             (this.disableTooltips ? '' : ' title="{text}"') +
38225                             ' >{text}</span></a>'
38226                 );
38227             }
38228             
38229             switch (typeof(template)) {
38230                 case 'object' :
38231                     break;
38232                 case 'string' :
38233                     template = new Roo.Template(template);
38234                     break;
38235                 default :
38236                     break;
38237             }
38238             
38239             var el = template.overwrite(td, {"text": text});
38240             
38241             var inner = el.getElementsByTagName("span")[0];
38242             
38243             return {"el": el, "inner": inner};
38244             
38245     }
38246         
38247     
38248 });
38249
38250 /**
38251  * @class Roo.TabPanelItem
38252  * @extends Roo.util.Observable
38253  * Represents an individual item (tab plus body) in a TabPanel.
38254  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38255  * @param {String} id The id of this TabPanelItem
38256  * @param {String} text The text for the tab of this TabPanelItem
38257  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38258  */
38259 Roo.bootstrap.panel.TabItem = function(config){
38260     /**
38261      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38262      * @type Roo.TabPanel
38263      */
38264     this.tabPanel = config.panel;
38265     /**
38266      * The id for this TabPanelItem
38267      * @type String
38268      */
38269     this.id = config.id;
38270     /** @private */
38271     this.disabled = false;
38272     /** @private */
38273     this.text = config.text;
38274     /** @private */
38275     this.loaded = false;
38276     this.closable = config.closable;
38277
38278     /**
38279      * The body element for this TabPanelItem.
38280      * @type Roo.Element
38281      */
38282     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38283     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38284     this.bodyEl.setStyle("display", "block");
38285     this.bodyEl.setStyle("zoom", "1");
38286     //this.hideAction();
38287
38288     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38289     /** @private */
38290     this.el = Roo.get(els.el);
38291     this.inner = Roo.get(els.inner, true);
38292     this.textEl = Roo.get(this.el.dom.firstChild, true);
38293     this.pnode = Roo.get(els.el.parentNode, true);
38294 //    this.el.on("mousedown", this.onTabMouseDown, this);
38295     this.el.on("click", this.onTabClick, this);
38296     /** @private */
38297     if(config.closable){
38298         var c = Roo.get(els.close, true);
38299         c.dom.title = this.closeText;
38300         c.addClassOnOver("close-over");
38301         c.on("click", this.closeClick, this);
38302      }
38303
38304     this.addEvents({
38305          /**
38306          * @event activate
38307          * Fires when this tab becomes the active tab.
38308          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38309          * @param {Roo.TabPanelItem} this
38310          */
38311         "activate": true,
38312         /**
38313          * @event beforeclose
38314          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38315          * @param {Roo.TabPanelItem} this
38316          * @param {Object} e Set cancel to true on this object to cancel the close.
38317          */
38318         "beforeclose": true,
38319         /**
38320          * @event close
38321          * Fires when this tab is closed.
38322          * @param {Roo.TabPanelItem} this
38323          */
38324          "close": true,
38325         /**
38326          * @event deactivate
38327          * Fires when this tab is no longer the active tab.
38328          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38329          * @param {Roo.TabPanelItem} this
38330          */
38331          "deactivate" : true
38332     });
38333     this.hidden = false;
38334
38335     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38336 };
38337
38338 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38339            {
38340     purgeListeners : function(){
38341        Roo.util.Observable.prototype.purgeListeners.call(this);
38342        this.el.removeAllListeners();
38343     },
38344     /**
38345      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38346      */
38347     show : function(){
38348         this.pnode.addClass("active");
38349         this.showAction();
38350         if(Roo.isOpera){
38351             this.tabPanel.stripWrap.repaint();
38352         }
38353         this.fireEvent("activate", this.tabPanel, this);
38354     },
38355
38356     /**
38357      * Returns true if this tab is the active tab.
38358      * @return {Boolean}
38359      */
38360     isActive : function(){
38361         return this.tabPanel.getActiveTab() == this;
38362     },
38363
38364     /**
38365      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38366      */
38367     hide : function(){
38368         this.pnode.removeClass("active");
38369         this.hideAction();
38370         this.fireEvent("deactivate", this.tabPanel, this);
38371     },
38372
38373     hideAction : function(){
38374         this.bodyEl.hide();
38375         this.bodyEl.setStyle("position", "absolute");
38376         this.bodyEl.setLeft("-20000px");
38377         this.bodyEl.setTop("-20000px");
38378     },
38379
38380     showAction : function(){
38381         this.bodyEl.setStyle("position", "relative");
38382         this.bodyEl.setTop("");
38383         this.bodyEl.setLeft("");
38384         this.bodyEl.show();
38385     },
38386
38387     /**
38388      * Set the tooltip for the tab.
38389      * @param {String} tooltip The tab's tooltip
38390      */
38391     setTooltip : function(text){
38392         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38393             this.textEl.dom.qtip = text;
38394             this.textEl.dom.removeAttribute('title');
38395         }else{
38396             this.textEl.dom.title = text;
38397         }
38398     },
38399
38400     onTabClick : function(e){
38401         e.preventDefault();
38402         this.tabPanel.activate(this.id);
38403     },
38404
38405     onTabMouseDown : function(e){
38406         e.preventDefault();
38407         this.tabPanel.activate(this.id);
38408     },
38409 /*
38410     getWidth : function(){
38411         return this.inner.getWidth();
38412     },
38413
38414     setWidth : function(width){
38415         var iwidth = width - this.pnode.getPadding("lr");
38416         this.inner.setWidth(iwidth);
38417         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38418         this.pnode.setWidth(width);
38419     },
38420 */
38421     /**
38422      * Show or hide the tab
38423      * @param {Boolean} hidden True to hide or false to show.
38424      */
38425     setHidden : function(hidden){
38426         this.hidden = hidden;
38427         this.pnode.setStyle("display", hidden ? "none" : "");
38428     },
38429
38430     /**
38431      * Returns true if this tab is "hidden"
38432      * @return {Boolean}
38433      */
38434     isHidden : function(){
38435         return this.hidden;
38436     },
38437
38438     /**
38439      * Returns the text for this tab
38440      * @return {String}
38441      */
38442     getText : function(){
38443         return this.text;
38444     },
38445     /*
38446     autoSize : function(){
38447         //this.el.beginMeasure();
38448         this.textEl.setWidth(1);
38449         /*
38450          *  #2804 [new] Tabs in Roojs
38451          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38452          */
38453         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38454         //this.el.endMeasure();
38455     //},
38456
38457     /**
38458      * Sets the text for the tab (Note: this also sets the tooltip text)
38459      * @param {String} text The tab's text and tooltip
38460      */
38461     setText : function(text){
38462         this.text = text;
38463         this.textEl.update(text);
38464         this.setTooltip(text);
38465         //if(!this.tabPanel.resizeTabs){
38466         //    this.autoSize();
38467         //}
38468     },
38469     /**
38470      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38471      */
38472     activate : function(){
38473         this.tabPanel.activate(this.id);
38474     },
38475
38476     /**
38477      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38478      */
38479     disable : function(){
38480         if(this.tabPanel.active != this){
38481             this.disabled = true;
38482             this.pnode.addClass("disabled");
38483         }
38484     },
38485
38486     /**
38487      * Enables this TabPanelItem if it was previously disabled.
38488      */
38489     enable : function(){
38490         this.disabled = false;
38491         this.pnode.removeClass("disabled");
38492     },
38493
38494     /**
38495      * Sets the content for this TabPanelItem.
38496      * @param {String} content The content
38497      * @param {Boolean} loadScripts true to look for and load scripts
38498      */
38499     setContent : function(content, loadScripts){
38500         this.bodyEl.update(content, loadScripts);
38501     },
38502
38503     /**
38504      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38505      * @return {Roo.UpdateManager} The UpdateManager
38506      */
38507     getUpdateManager : function(){
38508         return this.bodyEl.getUpdateManager();
38509     },
38510
38511     /**
38512      * Set a URL to be used to load the content for this TabPanelItem.
38513      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38514      * @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)
38515      * @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)
38516      * @return {Roo.UpdateManager} The UpdateManager
38517      */
38518     setUrl : function(url, params, loadOnce){
38519         if(this.refreshDelegate){
38520             this.un('activate', this.refreshDelegate);
38521         }
38522         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38523         this.on("activate", this.refreshDelegate);
38524         return this.bodyEl.getUpdateManager();
38525     },
38526
38527     /** @private */
38528     _handleRefresh : function(url, params, loadOnce){
38529         if(!loadOnce || !this.loaded){
38530             var updater = this.bodyEl.getUpdateManager();
38531             updater.update(url, params, this._setLoaded.createDelegate(this));
38532         }
38533     },
38534
38535     /**
38536      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38537      *   Will fail silently if the setUrl method has not been called.
38538      *   This does not activate the panel, just updates its content.
38539      */
38540     refresh : function(){
38541         if(this.refreshDelegate){
38542            this.loaded = false;
38543            this.refreshDelegate();
38544         }
38545     },
38546
38547     /** @private */
38548     _setLoaded : function(){
38549         this.loaded = true;
38550     },
38551
38552     /** @private */
38553     closeClick : function(e){
38554         var o = {};
38555         e.stopEvent();
38556         this.fireEvent("beforeclose", this, o);
38557         if(o.cancel !== true){
38558             this.tabPanel.removeTab(this.id);
38559         }
38560     },
38561     /**
38562      * The text displayed in the tooltip for the close icon.
38563      * @type String
38564      */
38565     closeText : "Close this tab"
38566 });
38567 /**
38568 *    This script refer to:
38569 *    Title: International Telephone Input
38570 *    Author: Jack O'Connor
38571 *    Code version:  v12.1.12
38572 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38573 **/
38574
38575 Roo.bootstrap.PhoneInputData = function() {
38576     var d = [
38577       [
38578         "Afghanistan (‫افغانستان‬‎)",
38579         "af",
38580         "93"
38581       ],
38582       [
38583         "Albania (Shqipëri)",
38584         "al",
38585         "355"
38586       ],
38587       [
38588         "Algeria (‫الجزائر‬‎)",
38589         "dz",
38590         "213"
38591       ],
38592       [
38593         "American Samoa",
38594         "as",
38595         "1684"
38596       ],
38597       [
38598         "Andorra",
38599         "ad",
38600         "376"
38601       ],
38602       [
38603         "Angola",
38604         "ao",
38605         "244"
38606       ],
38607       [
38608         "Anguilla",
38609         "ai",
38610         "1264"
38611       ],
38612       [
38613         "Antigua and Barbuda",
38614         "ag",
38615         "1268"
38616       ],
38617       [
38618         "Argentina",
38619         "ar",
38620         "54"
38621       ],
38622       [
38623         "Armenia (Հայաստան)",
38624         "am",
38625         "374"
38626       ],
38627       [
38628         "Aruba",
38629         "aw",
38630         "297"
38631       ],
38632       [
38633         "Australia",
38634         "au",
38635         "61",
38636         0
38637       ],
38638       [
38639         "Austria (Österreich)",
38640         "at",
38641         "43"
38642       ],
38643       [
38644         "Azerbaijan (Azərbaycan)",
38645         "az",
38646         "994"
38647       ],
38648       [
38649         "Bahamas",
38650         "bs",
38651         "1242"
38652       ],
38653       [
38654         "Bahrain (‫البحرين‬‎)",
38655         "bh",
38656         "973"
38657       ],
38658       [
38659         "Bangladesh (বাংলাদেশ)",
38660         "bd",
38661         "880"
38662       ],
38663       [
38664         "Barbados",
38665         "bb",
38666         "1246"
38667       ],
38668       [
38669         "Belarus (Беларусь)",
38670         "by",
38671         "375"
38672       ],
38673       [
38674         "Belgium (België)",
38675         "be",
38676         "32"
38677       ],
38678       [
38679         "Belize",
38680         "bz",
38681         "501"
38682       ],
38683       [
38684         "Benin (Bénin)",
38685         "bj",
38686         "229"
38687       ],
38688       [
38689         "Bermuda",
38690         "bm",
38691         "1441"
38692       ],
38693       [
38694         "Bhutan (འབྲུག)",
38695         "bt",
38696         "975"
38697       ],
38698       [
38699         "Bolivia",
38700         "bo",
38701         "591"
38702       ],
38703       [
38704         "Bosnia and Herzegovina (Босна и Херцеговина)",
38705         "ba",
38706         "387"
38707       ],
38708       [
38709         "Botswana",
38710         "bw",
38711         "267"
38712       ],
38713       [
38714         "Brazil (Brasil)",
38715         "br",
38716         "55"
38717       ],
38718       [
38719         "British Indian Ocean Territory",
38720         "io",
38721         "246"
38722       ],
38723       [
38724         "British Virgin Islands",
38725         "vg",
38726         "1284"
38727       ],
38728       [
38729         "Brunei",
38730         "bn",
38731         "673"
38732       ],
38733       [
38734         "Bulgaria (България)",
38735         "bg",
38736         "359"
38737       ],
38738       [
38739         "Burkina Faso",
38740         "bf",
38741         "226"
38742       ],
38743       [
38744         "Burundi (Uburundi)",
38745         "bi",
38746         "257"
38747       ],
38748       [
38749         "Cambodia (កម្ពុជា)",
38750         "kh",
38751         "855"
38752       ],
38753       [
38754         "Cameroon (Cameroun)",
38755         "cm",
38756         "237"
38757       ],
38758       [
38759         "Canada",
38760         "ca",
38761         "1",
38762         1,
38763         ["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"]
38764       ],
38765       [
38766         "Cape Verde (Kabu Verdi)",
38767         "cv",
38768         "238"
38769       ],
38770       [
38771         "Caribbean Netherlands",
38772         "bq",
38773         "599",
38774         1
38775       ],
38776       [
38777         "Cayman Islands",
38778         "ky",
38779         "1345"
38780       ],
38781       [
38782         "Central African Republic (République centrafricaine)",
38783         "cf",
38784         "236"
38785       ],
38786       [
38787         "Chad (Tchad)",
38788         "td",
38789         "235"
38790       ],
38791       [
38792         "Chile",
38793         "cl",
38794         "56"
38795       ],
38796       [
38797         "China (中国)",
38798         "cn",
38799         "86"
38800       ],
38801       [
38802         "Christmas Island",
38803         "cx",
38804         "61",
38805         2
38806       ],
38807       [
38808         "Cocos (Keeling) Islands",
38809         "cc",
38810         "61",
38811         1
38812       ],
38813       [
38814         "Colombia",
38815         "co",
38816         "57"
38817       ],
38818       [
38819         "Comoros (‫جزر القمر‬‎)",
38820         "km",
38821         "269"
38822       ],
38823       [
38824         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38825         "cd",
38826         "243"
38827       ],
38828       [
38829         "Congo (Republic) (Congo-Brazzaville)",
38830         "cg",
38831         "242"
38832       ],
38833       [
38834         "Cook Islands",
38835         "ck",
38836         "682"
38837       ],
38838       [
38839         "Costa Rica",
38840         "cr",
38841         "506"
38842       ],
38843       [
38844         "Côte d’Ivoire",
38845         "ci",
38846         "225"
38847       ],
38848       [
38849         "Croatia (Hrvatska)",
38850         "hr",
38851         "385"
38852       ],
38853       [
38854         "Cuba",
38855         "cu",
38856         "53"
38857       ],
38858       [
38859         "Curaçao",
38860         "cw",
38861         "599",
38862         0
38863       ],
38864       [
38865         "Cyprus (Κύπρος)",
38866         "cy",
38867         "357"
38868       ],
38869       [
38870         "Czech Republic (Česká republika)",
38871         "cz",
38872         "420"
38873       ],
38874       [
38875         "Denmark (Danmark)",
38876         "dk",
38877         "45"
38878       ],
38879       [
38880         "Djibouti",
38881         "dj",
38882         "253"
38883       ],
38884       [
38885         "Dominica",
38886         "dm",
38887         "1767"
38888       ],
38889       [
38890         "Dominican Republic (República Dominicana)",
38891         "do",
38892         "1",
38893         2,
38894         ["809", "829", "849"]
38895       ],
38896       [
38897         "Ecuador",
38898         "ec",
38899         "593"
38900       ],
38901       [
38902         "Egypt (‫مصر‬‎)",
38903         "eg",
38904         "20"
38905       ],
38906       [
38907         "El Salvador",
38908         "sv",
38909         "503"
38910       ],
38911       [
38912         "Equatorial Guinea (Guinea Ecuatorial)",
38913         "gq",
38914         "240"
38915       ],
38916       [
38917         "Eritrea",
38918         "er",
38919         "291"
38920       ],
38921       [
38922         "Estonia (Eesti)",
38923         "ee",
38924         "372"
38925       ],
38926       [
38927         "Ethiopia",
38928         "et",
38929         "251"
38930       ],
38931       [
38932         "Falkland Islands (Islas Malvinas)",
38933         "fk",
38934         "500"
38935       ],
38936       [
38937         "Faroe Islands (Føroyar)",
38938         "fo",
38939         "298"
38940       ],
38941       [
38942         "Fiji",
38943         "fj",
38944         "679"
38945       ],
38946       [
38947         "Finland (Suomi)",
38948         "fi",
38949         "358",
38950         0
38951       ],
38952       [
38953         "France",
38954         "fr",
38955         "33"
38956       ],
38957       [
38958         "French Guiana (Guyane française)",
38959         "gf",
38960         "594"
38961       ],
38962       [
38963         "French Polynesia (Polynésie française)",
38964         "pf",
38965         "689"
38966       ],
38967       [
38968         "Gabon",
38969         "ga",
38970         "241"
38971       ],
38972       [
38973         "Gambia",
38974         "gm",
38975         "220"
38976       ],
38977       [
38978         "Georgia (საქართველო)",
38979         "ge",
38980         "995"
38981       ],
38982       [
38983         "Germany (Deutschland)",
38984         "de",
38985         "49"
38986       ],
38987       [
38988         "Ghana (Gaana)",
38989         "gh",
38990         "233"
38991       ],
38992       [
38993         "Gibraltar",
38994         "gi",
38995         "350"
38996       ],
38997       [
38998         "Greece (Ελλάδα)",
38999         "gr",
39000         "30"
39001       ],
39002       [
39003         "Greenland (Kalaallit Nunaat)",
39004         "gl",
39005         "299"
39006       ],
39007       [
39008         "Grenada",
39009         "gd",
39010         "1473"
39011       ],
39012       [
39013         "Guadeloupe",
39014         "gp",
39015         "590",
39016         0
39017       ],
39018       [
39019         "Guam",
39020         "gu",
39021         "1671"
39022       ],
39023       [
39024         "Guatemala",
39025         "gt",
39026         "502"
39027       ],
39028       [
39029         "Guernsey",
39030         "gg",
39031         "44",
39032         1
39033       ],
39034       [
39035         "Guinea (Guinée)",
39036         "gn",
39037         "224"
39038       ],
39039       [
39040         "Guinea-Bissau (Guiné Bissau)",
39041         "gw",
39042         "245"
39043       ],
39044       [
39045         "Guyana",
39046         "gy",
39047         "592"
39048       ],
39049       [
39050         "Haiti",
39051         "ht",
39052         "509"
39053       ],
39054       [
39055         "Honduras",
39056         "hn",
39057         "504"
39058       ],
39059       [
39060         "Hong Kong (香港)",
39061         "hk",
39062         "852"
39063       ],
39064       [
39065         "Hungary (Magyarország)",
39066         "hu",
39067         "36"
39068       ],
39069       [
39070         "Iceland (Ísland)",
39071         "is",
39072         "354"
39073       ],
39074       [
39075         "India (भारत)",
39076         "in",
39077         "91"
39078       ],
39079       [
39080         "Indonesia",
39081         "id",
39082         "62"
39083       ],
39084       [
39085         "Iran (‫ایران‬‎)",
39086         "ir",
39087         "98"
39088       ],
39089       [
39090         "Iraq (‫العراق‬‎)",
39091         "iq",
39092         "964"
39093       ],
39094       [
39095         "Ireland",
39096         "ie",
39097         "353"
39098       ],
39099       [
39100         "Isle of Man",
39101         "im",
39102         "44",
39103         2
39104       ],
39105       [
39106         "Israel (‫ישראל‬‎)",
39107         "il",
39108         "972"
39109       ],
39110       [
39111         "Italy (Italia)",
39112         "it",
39113         "39",
39114         0
39115       ],
39116       [
39117         "Jamaica",
39118         "jm",
39119         "1876"
39120       ],
39121       [
39122         "Japan (日本)",
39123         "jp",
39124         "81"
39125       ],
39126       [
39127         "Jersey",
39128         "je",
39129         "44",
39130         3
39131       ],
39132       [
39133         "Jordan (‫الأردن‬‎)",
39134         "jo",
39135         "962"
39136       ],
39137       [
39138         "Kazakhstan (Казахстан)",
39139         "kz",
39140         "7",
39141         1
39142       ],
39143       [
39144         "Kenya",
39145         "ke",
39146         "254"
39147       ],
39148       [
39149         "Kiribati",
39150         "ki",
39151         "686"
39152       ],
39153       [
39154         "Kosovo",
39155         "xk",
39156         "383"
39157       ],
39158       [
39159         "Kuwait (‫الكويت‬‎)",
39160         "kw",
39161         "965"
39162       ],
39163       [
39164         "Kyrgyzstan (Кыргызстан)",
39165         "kg",
39166         "996"
39167       ],
39168       [
39169         "Laos (ລາວ)",
39170         "la",
39171         "856"
39172       ],
39173       [
39174         "Latvia (Latvija)",
39175         "lv",
39176         "371"
39177       ],
39178       [
39179         "Lebanon (‫لبنان‬‎)",
39180         "lb",
39181         "961"
39182       ],
39183       [
39184         "Lesotho",
39185         "ls",
39186         "266"
39187       ],
39188       [
39189         "Liberia",
39190         "lr",
39191         "231"
39192       ],
39193       [
39194         "Libya (‫ليبيا‬‎)",
39195         "ly",
39196         "218"
39197       ],
39198       [
39199         "Liechtenstein",
39200         "li",
39201         "423"
39202       ],
39203       [
39204         "Lithuania (Lietuva)",
39205         "lt",
39206         "370"
39207       ],
39208       [
39209         "Luxembourg",
39210         "lu",
39211         "352"
39212       ],
39213       [
39214         "Macau (澳門)",
39215         "mo",
39216         "853"
39217       ],
39218       [
39219         "Macedonia (FYROM) (Македонија)",
39220         "mk",
39221         "389"
39222       ],
39223       [
39224         "Madagascar (Madagasikara)",
39225         "mg",
39226         "261"
39227       ],
39228       [
39229         "Malawi",
39230         "mw",
39231         "265"
39232       ],
39233       [
39234         "Malaysia",
39235         "my",
39236         "60"
39237       ],
39238       [
39239         "Maldives",
39240         "mv",
39241         "960"
39242       ],
39243       [
39244         "Mali",
39245         "ml",
39246         "223"
39247       ],
39248       [
39249         "Malta",
39250         "mt",
39251         "356"
39252       ],
39253       [
39254         "Marshall Islands",
39255         "mh",
39256         "692"
39257       ],
39258       [
39259         "Martinique",
39260         "mq",
39261         "596"
39262       ],
39263       [
39264         "Mauritania (‫موريتانيا‬‎)",
39265         "mr",
39266         "222"
39267       ],
39268       [
39269         "Mauritius (Moris)",
39270         "mu",
39271         "230"
39272       ],
39273       [
39274         "Mayotte",
39275         "yt",
39276         "262",
39277         1
39278       ],
39279       [
39280         "Mexico (México)",
39281         "mx",
39282         "52"
39283       ],
39284       [
39285         "Micronesia",
39286         "fm",
39287         "691"
39288       ],
39289       [
39290         "Moldova (Republica Moldova)",
39291         "md",
39292         "373"
39293       ],
39294       [
39295         "Monaco",
39296         "mc",
39297         "377"
39298       ],
39299       [
39300         "Mongolia (Монгол)",
39301         "mn",
39302         "976"
39303       ],
39304       [
39305         "Montenegro (Crna Gora)",
39306         "me",
39307         "382"
39308       ],
39309       [
39310         "Montserrat",
39311         "ms",
39312         "1664"
39313       ],
39314       [
39315         "Morocco (‫المغرب‬‎)",
39316         "ma",
39317         "212",
39318         0
39319       ],
39320       [
39321         "Mozambique (Moçambique)",
39322         "mz",
39323         "258"
39324       ],
39325       [
39326         "Myanmar (Burma) (မြန်မာ)",
39327         "mm",
39328         "95"
39329       ],
39330       [
39331         "Namibia (Namibië)",
39332         "na",
39333         "264"
39334       ],
39335       [
39336         "Nauru",
39337         "nr",
39338         "674"
39339       ],
39340       [
39341         "Nepal (नेपाल)",
39342         "np",
39343         "977"
39344       ],
39345       [
39346         "Netherlands (Nederland)",
39347         "nl",
39348         "31"
39349       ],
39350       [
39351         "New Caledonia (Nouvelle-Calédonie)",
39352         "nc",
39353         "687"
39354       ],
39355       [
39356         "New Zealand",
39357         "nz",
39358         "64"
39359       ],
39360       [
39361         "Nicaragua",
39362         "ni",
39363         "505"
39364       ],
39365       [
39366         "Niger (Nijar)",
39367         "ne",
39368         "227"
39369       ],
39370       [
39371         "Nigeria",
39372         "ng",
39373         "234"
39374       ],
39375       [
39376         "Niue",
39377         "nu",
39378         "683"
39379       ],
39380       [
39381         "Norfolk Island",
39382         "nf",
39383         "672"
39384       ],
39385       [
39386         "North Korea (조선 민주주의 인민 공화국)",
39387         "kp",
39388         "850"
39389       ],
39390       [
39391         "Northern Mariana Islands",
39392         "mp",
39393         "1670"
39394       ],
39395       [
39396         "Norway (Norge)",
39397         "no",
39398         "47",
39399         0
39400       ],
39401       [
39402         "Oman (‫عُمان‬‎)",
39403         "om",
39404         "968"
39405       ],
39406       [
39407         "Pakistan (‫پاکستان‬‎)",
39408         "pk",
39409         "92"
39410       ],
39411       [
39412         "Palau",
39413         "pw",
39414         "680"
39415       ],
39416       [
39417         "Palestine (‫فلسطين‬‎)",
39418         "ps",
39419         "970"
39420       ],
39421       [
39422         "Panama (Panamá)",
39423         "pa",
39424         "507"
39425       ],
39426       [
39427         "Papua New Guinea",
39428         "pg",
39429         "675"
39430       ],
39431       [
39432         "Paraguay",
39433         "py",
39434         "595"
39435       ],
39436       [
39437         "Peru (Perú)",
39438         "pe",
39439         "51"
39440       ],
39441       [
39442         "Philippines",
39443         "ph",
39444         "63"
39445       ],
39446       [
39447         "Poland (Polska)",
39448         "pl",
39449         "48"
39450       ],
39451       [
39452         "Portugal",
39453         "pt",
39454         "351"
39455       ],
39456       [
39457         "Puerto Rico",
39458         "pr",
39459         "1",
39460         3,
39461         ["787", "939"]
39462       ],
39463       [
39464         "Qatar (‫قطر‬‎)",
39465         "qa",
39466         "974"
39467       ],
39468       [
39469         "Réunion (La Réunion)",
39470         "re",
39471         "262",
39472         0
39473       ],
39474       [
39475         "Romania (România)",
39476         "ro",
39477         "40"
39478       ],
39479       [
39480         "Russia (Россия)",
39481         "ru",
39482         "7",
39483         0
39484       ],
39485       [
39486         "Rwanda",
39487         "rw",
39488         "250"
39489       ],
39490       [
39491         "Saint Barthélemy",
39492         "bl",
39493         "590",
39494         1
39495       ],
39496       [
39497         "Saint Helena",
39498         "sh",
39499         "290"
39500       ],
39501       [
39502         "Saint Kitts and Nevis",
39503         "kn",
39504         "1869"
39505       ],
39506       [
39507         "Saint Lucia",
39508         "lc",
39509         "1758"
39510       ],
39511       [
39512         "Saint Martin (Saint-Martin (partie française))",
39513         "mf",
39514         "590",
39515         2
39516       ],
39517       [
39518         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39519         "pm",
39520         "508"
39521       ],
39522       [
39523         "Saint Vincent and the Grenadines",
39524         "vc",
39525         "1784"
39526       ],
39527       [
39528         "Samoa",
39529         "ws",
39530         "685"
39531       ],
39532       [
39533         "San Marino",
39534         "sm",
39535         "378"
39536       ],
39537       [
39538         "São Tomé and Príncipe (São Tomé e Príncipe)",
39539         "st",
39540         "239"
39541       ],
39542       [
39543         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39544         "sa",
39545         "966"
39546       ],
39547       [
39548         "Senegal (Sénégal)",
39549         "sn",
39550         "221"
39551       ],
39552       [
39553         "Serbia (Србија)",
39554         "rs",
39555         "381"
39556       ],
39557       [
39558         "Seychelles",
39559         "sc",
39560         "248"
39561       ],
39562       [
39563         "Sierra Leone",
39564         "sl",
39565         "232"
39566       ],
39567       [
39568         "Singapore",
39569         "sg",
39570         "65"
39571       ],
39572       [
39573         "Sint Maarten",
39574         "sx",
39575         "1721"
39576       ],
39577       [
39578         "Slovakia (Slovensko)",
39579         "sk",
39580         "421"
39581       ],
39582       [
39583         "Slovenia (Slovenija)",
39584         "si",
39585         "386"
39586       ],
39587       [
39588         "Solomon Islands",
39589         "sb",
39590         "677"
39591       ],
39592       [
39593         "Somalia (Soomaaliya)",
39594         "so",
39595         "252"
39596       ],
39597       [
39598         "South Africa",
39599         "za",
39600         "27"
39601       ],
39602       [
39603         "South Korea (대한민국)",
39604         "kr",
39605         "82"
39606       ],
39607       [
39608         "South Sudan (‫جنوب السودان‬‎)",
39609         "ss",
39610         "211"
39611       ],
39612       [
39613         "Spain (España)",
39614         "es",
39615         "34"
39616       ],
39617       [
39618         "Sri Lanka (ශ්‍රී ලංකාව)",
39619         "lk",
39620         "94"
39621       ],
39622       [
39623         "Sudan (‫السودان‬‎)",
39624         "sd",
39625         "249"
39626       ],
39627       [
39628         "Suriname",
39629         "sr",
39630         "597"
39631       ],
39632       [
39633         "Svalbard and Jan Mayen",
39634         "sj",
39635         "47",
39636         1
39637       ],
39638       [
39639         "Swaziland",
39640         "sz",
39641         "268"
39642       ],
39643       [
39644         "Sweden (Sverige)",
39645         "se",
39646         "46"
39647       ],
39648       [
39649         "Switzerland (Schweiz)",
39650         "ch",
39651         "41"
39652       ],
39653       [
39654         "Syria (‫سوريا‬‎)",
39655         "sy",
39656         "963"
39657       ],
39658       [
39659         "Taiwan (台灣)",
39660         "tw",
39661         "886"
39662       ],
39663       [
39664         "Tajikistan",
39665         "tj",
39666         "992"
39667       ],
39668       [
39669         "Tanzania",
39670         "tz",
39671         "255"
39672       ],
39673       [
39674         "Thailand (ไทย)",
39675         "th",
39676         "66"
39677       ],
39678       [
39679         "Timor-Leste",
39680         "tl",
39681         "670"
39682       ],
39683       [
39684         "Togo",
39685         "tg",
39686         "228"
39687       ],
39688       [
39689         "Tokelau",
39690         "tk",
39691         "690"
39692       ],
39693       [
39694         "Tonga",
39695         "to",
39696         "676"
39697       ],
39698       [
39699         "Trinidad and Tobago",
39700         "tt",
39701         "1868"
39702       ],
39703       [
39704         "Tunisia (‫تونس‬‎)",
39705         "tn",
39706         "216"
39707       ],
39708       [
39709         "Turkey (Türkiye)",
39710         "tr",
39711         "90"
39712       ],
39713       [
39714         "Turkmenistan",
39715         "tm",
39716         "993"
39717       ],
39718       [
39719         "Turks and Caicos Islands",
39720         "tc",
39721         "1649"
39722       ],
39723       [
39724         "Tuvalu",
39725         "tv",
39726         "688"
39727       ],
39728       [
39729         "U.S. Virgin Islands",
39730         "vi",
39731         "1340"
39732       ],
39733       [
39734         "Uganda",
39735         "ug",
39736         "256"
39737       ],
39738       [
39739         "Ukraine (Україна)",
39740         "ua",
39741         "380"
39742       ],
39743       [
39744         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39745         "ae",
39746         "971"
39747       ],
39748       [
39749         "United Kingdom",
39750         "gb",
39751         "44",
39752         0
39753       ],
39754       [
39755         "United States",
39756         "us",
39757         "1",
39758         0
39759       ],
39760       [
39761         "Uruguay",
39762         "uy",
39763         "598"
39764       ],
39765       [
39766         "Uzbekistan (Oʻzbekiston)",
39767         "uz",
39768         "998"
39769       ],
39770       [
39771         "Vanuatu",
39772         "vu",
39773         "678"
39774       ],
39775       [
39776         "Vatican City (Città del Vaticano)",
39777         "va",
39778         "39",
39779         1
39780       ],
39781       [
39782         "Venezuela",
39783         "ve",
39784         "58"
39785       ],
39786       [
39787         "Vietnam (Việt Nam)",
39788         "vn",
39789         "84"
39790       ],
39791       [
39792         "Wallis and Futuna (Wallis-et-Futuna)",
39793         "wf",
39794         "681"
39795       ],
39796       [
39797         "Western Sahara (‫الصحراء الغربية‬‎)",
39798         "eh",
39799         "212",
39800         1
39801       ],
39802       [
39803         "Yemen (‫اليمن‬‎)",
39804         "ye",
39805         "967"
39806       ],
39807       [
39808         "Zambia",
39809         "zm",
39810         "260"
39811       ],
39812       [
39813         "Zimbabwe",
39814         "zw",
39815         "263"
39816       ],
39817       [
39818         "Åland Islands",
39819         "ax",
39820         "358",
39821         1
39822       ]
39823   ];
39824   
39825   return d;
39826 }/**
39827 *    This script refer to:
39828 *    Title: International Telephone Input
39829 *    Author: Jack O'Connor
39830 *    Code version:  v12.1.12
39831 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39832 **/
39833
39834 /**
39835  * @class Roo.bootstrap.PhoneInput
39836  * @extends Roo.bootstrap.TriggerField
39837  * An input with International dial-code selection
39838  
39839  * @cfg {String} defaultDialCode default '+852'
39840  * @cfg {Array} preferedCountries default []
39841   
39842  * @constructor
39843  * Create a new PhoneInput.
39844  * @param {Object} config Configuration options
39845  */
39846
39847 Roo.bootstrap.PhoneInput = function(config) {
39848     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39849 };
39850
39851 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39852         
39853         listWidth: undefined,
39854         
39855         selectedClass: 'active',
39856         
39857         invalidClass : "has-warning",
39858         
39859         validClass: 'has-success',
39860         
39861         allowed: '0123456789',
39862         
39863         /**
39864          * @cfg {String} defaultDialCode The default dial code when initializing the input
39865          */
39866         defaultDialCode: '+852',
39867         
39868         /**
39869          * @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
39870          */
39871         preferedCountries: false,
39872         
39873         getAutoCreate : function()
39874         {
39875             var data = Roo.bootstrap.PhoneInputData();
39876             var align = this.labelAlign || this.parentLabelAlign();
39877             var id = Roo.id();
39878             
39879             this.allCountries = [];
39880             this.dialCodeMapping = [];
39881             
39882             for (var i = 0; i < data.length; i++) {
39883               var c = data[i];
39884               this.allCountries[i] = {
39885                 name: c[0],
39886                 iso2: c[1],
39887                 dialCode: c[2],
39888                 priority: c[3] || 0,
39889                 areaCodes: c[4] || null
39890               };
39891               this.dialCodeMapping[c[2]] = {
39892                   name: c[0],
39893                   iso2: c[1],
39894                   priority: c[3] || 0,
39895                   areaCodes: c[4] || null
39896               };
39897             }
39898             
39899             var cfg = {
39900                 cls: 'form-group',
39901                 cn: []
39902             };
39903             
39904             var input =  {
39905                 tag: 'input',
39906                 id : id,
39907                 cls : 'form-control tel-input',
39908                 autocomplete: 'new-password'
39909             };
39910             
39911             var hiddenInput = {
39912                 tag: 'input',
39913                 type: 'hidden',
39914                 cls: 'hidden-tel-input'
39915             };
39916             
39917             if (this.name) {
39918                 hiddenInput.name = this.name;
39919             }
39920             
39921             if (this.disabled) {
39922                 input.disabled = true;
39923             }
39924             
39925             var flag_container = {
39926                 tag: 'div',
39927                 cls: 'flag-box',
39928                 cn: [
39929                     {
39930                         tag: 'div',
39931                         cls: 'flag'
39932                     },
39933                     {
39934                         tag: 'div',
39935                         cls: 'caret'
39936                     }
39937                 ]
39938             };
39939             
39940             var box = {
39941                 tag: 'div',
39942                 cls: this.hasFeedback ? 'has-feedback' : '',
39943                 cn: [
39944                     hiddenInput,
39945                     input,
39946                     {
39947                         tag: 'input',
39948                         cls: 'dial-code-holder',
39949                         disabled: true
39950                     }
39951                 ]
39952             };
39953             
39954             var container = {
39955                 cls: 'roo-select2-container input-group',
39956                 cn: [
39957                     flag_container,
39958                     box
39959                 ]
39960             };
39961             
39962             if (this.fieldLabel.length) {
39963                 var indicator = {
39964                     tag: 'i',
39965                     tooltip: 'This field is required'
39966                 };
39967                 
39968                 var label = {
39969                     tag: 'label',
39970                     'for':  id,
39971                     cls: 'control-label',
39972                     cn: []
39973                 };
39974                 
39975                 var label_text = {
39976                     tag: 'span',
39977                     html: this.fieldLabel
39978                 };
39979                 
39980                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39981                 label.cn = [
39982                     indicator,
39983                     label_text
39984                 ];
39985                 
39986                 if(this.indicatorpos == 'right') {
39987                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39988                     label.cn = [
39989                         label_text,
39990                         indicator
39991                     ];
39992                 }
39993                 
39994                 if(align == 'left') {
39995                     container = {
39996                         tag: 'div',
39997                         cn: [
39998                             container
39999                         ]
40000                     };
40001                     
40002                     if(this.labelWidth > 12){
40003                         label.style = "width: " + this.labelWidth + 'px';
40004                     }
40005                     if(this.labelWidth < 13 && this.labelmd == 0){
40006                         this.labelmd = this.labelWidth;
40007                     }
40008                     if(this.labellg > 0){
40009                         label.cls += ' col-lg-' + this.labellg;
40010                         input.cls += ' col-lg-' + (12 - this.labellg);
40011                     }
40012                     if(this.labelmd > 0){
40013                         label.cls += ' col-md-' + this.labelmd;
40014                         container.cls += ' col-md-' + (12 - this.labelmd);
40015                     }
40016                     if(this.labelsm > 0){
40017                         label.cls += ' col-sm-' + this.labelsm;
40018                         container.cls += ' col-sm-' + (12 - this.labelsm);
40019                     }
40020                     if(this.labelxs > 0){
40021                         label.cls += ' col-xs-' + this.labelxs;
40022                         container.cls += ' col-xs-' + (12 - this.labelxs);
40023                     }
40024                 }
40025             }
40026             
40027             cfg.cn = [
40028                 label,
40029                 container
40030             ];
40031             
40032             var settings = this;
40033             
40034             ['xs','sm','md','lg'].map(function(size){
40035                 if (settings[size]) {
40036                     cfg.cls += ' col-' + size + '-' + settings[size];
40037                 }
40038             });
40039             
40040             this.store = new Roo.data.Store({
40041                 proxy : new Roo.data.MemoryProxy({}),
40042                 reader : new Roo.data.JsonReader({
40043                     fields : [
40044                         {
40045                             'name' : 'name',
40046                             'type' : 'string'
40047                         },
40048                         {
40049                             'name' : 'iso2',
40050                             'type' : 'string'
40051                         },
40052                         {
40053                             'name' : 'dialCode',
40054                             'type' : 'string'
40055                         },
40056                         {
40057                             'name' : 'priority',
40058                             'type' : 'string'
40059                         },
40060                         {
40061                             'name' : 'areaCodes',
40062                             'type' : 'string'
40063                         }
40064                     ]
40065                 })
40066             });
40067             
40068             if(!this.preferedCountries) {
40069                 this.preferedCountries = [
40070                     'hk',
40071                     'gb',
40072                     'us'
40073                 ];
40074             }
40075             
40076             var p = this.preferedCountries.reverse();
40077             
40078             if(p) {
40079                 for (var i = 0; i < p.length; i++) {
40080                     for (var j = 0; j < this.allCountries.length; j++) {
40081                         if(this.allCountries[j].iso2 == p[i]) {
40082                             var t = this.allCountries[j];
40083                             this.allCountries.splice(j,1);
40084                             this.allCountries.unshift(t);
40085                         }
40086                     } 
40087                 }
40088             }
40089             
40090             this.store.proxy.data = {
40091                 success: true,
40092                 data: this.allCountries
40093             };
40094             
40095             return cfg;
40096         },
40097         
40098         initEvents : function()
40099         {
40100             this.createList();
40101             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40102             
40103             this.indicator = this.indicatorEl();
40104             this.flag = this.flagEl();
40105             this.dialCodeHolder = this.dialCodeHolderEl();
40106             
40107             this.trigger = this.el.select('div.flag-box',true).first();
40108             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40109             
40110             var _this = this;
40111             
40112             (function(){
40113                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40114                 _this.list.setWidth(lw);
40115             }).defer(100);
40116             
40117             this.list.on('mouseover', this.onViewOver, this);
40118             this.list.on('mousemove', this.onViewMove, this);
40119             this.inputEl().on("keyup", this.onKeyUp, this);
40120             
40121             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40122
40123             this.view = new Roo.View(this.list, this.tpl, {
40124                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40125             });
40126             
40127             this.view.on('click', this.onViewClick, this);
40128             this.setValue(this.defaultDialCode);
40129         },
40130         
40131         onTriggerClick : function(e)
40132         {
40133             Roo.log('trigger click');
40134             if(this.disabled){
40135                 return;
40136             }
40137             
40138             if(this.isExpanded()){
40139                 this.collapse();
40140                 this.hasFocus = false;
40141             }else {
40142                 this.store.load({});
40143                 this.hasFocus = true;
40144                 this.expand();
40145             }
40146         },
40147         
40148         isExpanded : function()
40149         {
40150             return this.list.isVisible();
40151         },
40152         
40153         collapse : function()
40154         {
40155             if(!this.isExpanded()){
40156                 return;
40157             }
40158             this.list.hide();
40159             Roo.get(document).un('mousedown', this.collapseIf, this);
40160             Roo.get(document).un('mousewheel', this.collapseIf, this);
40161             this.fireEvent('collapse', this);
40162             this.validate();
40163         },
40164         
40165         expand : function()
40166         {
40167             Roo.log('expand');
40168
40169             if(this.isExpanded() || !this.hasFocus){
40170                 return;
40171             }
40172             
40173             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40174             this.list.setWidth(lw);
40175             
40176             this.list.show();
40177             this.restrictHeight();
40178             
40179             Roo.get(document).on('mousedown', this.collapseIf, this);
40180             Roo.get(document).on('mousewheel', this.collapseIf, this);
40181             
40182             this.fireEvent('expand', this);
40183         },
40184         
40185         restrictHeight : function()
40186         {
40187             this.list.alignTo(this.inputEl(), this.listAlign);
40188             this.list.alignTo(this.inputEl(), this.listAlign);
40189         },
40190         
40191         onViewOver : function(e, t)
40192         {
40193             if(this.inKeyMode){
40194                 return;
40195             }
40196             var item = this.view.findItemFromChild(t);
40197             
40198             if(item){
40199                 var index = this.view.indexOf(item);
40200                 this.select(index, false);
40201             }
40202         },
40203
40204         // private
40205         onViewClick : function(view, doFocus, el, e)
40206         {
40207             var index = this.view.getSelectedIndexes()[0];
40208             
40209             var r = this.store.getAt(index);
40210             
40211             if(r){
40212                 this.onSelect(r, index);
40213             }
40214             if(doFocus !== false && !this.blockFocus){
40215                 this.inputEl().focus();
40216             }
40217         },
40218         
40219         onViewMove : function(e, t)
40220         {
40221             this.inKeyMode = false;
40222         },
40223         
40224         select : function(index, scrollIntoView)
40225         {
40226             this.selectedIndex = index;
40227             this.view.select(index);
40228             if(scrollIntoView !== false){
40229                 var el = this.view.getNode(index);
40230                 if(el){
40231                     this.list.scrollChildIntoView(el, false);
40232                 }
40233             }
40234         },
40235         
40236         createList : function()
40237         {
40238             this.list = Roo.get(document.body).createChild({
40239                 tag: 'ul',
40240                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40241                 style: 'display:none'
40242             });
40243             
40244             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40245         },
40246         
40247         collapseIf : function(e)
40248         {
40249             var in_combo  = e.within(this.el);
40250             var in_list =  e.within(this.list);
40251             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40252             
40253             if (in_combo || in_list || is_list) {
40254                 return;
40255             }
40256             this.collapse();
40257         },
40258         
40259         onSelect : function(record, index)
40260         {
40261             if(this.fireEvent('beforeselect', this, record, index) !== false){
40262                 
40263                 this.setFlagClass(record.data.iso2);
40264                 this.setDialCode(record.data.dialCode);
40265                 this.hasFocus = false;
40266                 this.collapse();
40267                 this.fireEvent('select', this, record, index);
40268             }
40269         },
40270         
40271         flagEl : function()
40272         {
40273             var flag = this.el.select('div.flag',true).first();
40274             if(!flag){
40275                 return false;
40276             }
40277             return flag;
40278         },
40279         
40280         dialCodeHolderEl : function()
40281         {
40282             var d = this.el.select('input.dial-code-holder',true).first();
40283             if(!d){
40284                 return false;
40285             }
40286             return d;
40287         },
40288         
40289         setDialCode : function(v)
40290         {
40291             this.dialCodeHolder.dom.value = '+'+v;
40292         },
40293         
40294         setFlagClass : function(n)
40295         {
40296             this.flag.dom.className = 'flag '+n;
40297         },
40298         
40299         getValue : function()
40300         {
40301             var v = this.inputEl().getValue();
40302             if(this.dialCodeHolder) {
40303                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40304             }
40305             return v;
40306         },
40307         
40308         setValue : function(v)
40309         {
40310             var d = this.getDialCode(v);
40311             
40312             //invalid dial code
40313             if(v.length == 0 || !d || d.length == 0) {
40314                 if(this.rendered){
40315                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40316                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40317                 }
40318                 return;
40319             }
40320             
40321             //valid dial code
40322             this.setFlagClass(this.dialCodeMapping[d].iso2);
40323             this.setDialCode(d);
40324             this.inputEl().dom.value = v.replace('+'+d,'');
40325             this.hiddenEl().dom.value = this.getValue();
40326             
40327             this.validate();
40328         },
40329         
40330         getDialCode : function(v)
40331         {
40332             v = v ||  '';
40333             
40334             if (v.length == 0) {
40335                 return this.dialCodeHolder.dom.value;
40336             }
40337             
40338             var dialCode = "";
40339             if (v.charAt(0) != "+") {
40340                 return false;
40341             }
40342             var numericChars = "";
40343             for (var i = 1; i < v.length; i++) {
40344               var c = v.charAt(i);
40345               if (!isNaN(c)) {
40346                 numericChars += c;
40347                 if (this.dialCodeMapping[numericChars]) {
40348                   dialCode = v.substr(1, i);
40349                 }
40350                 if (numericChars.length == 4) {
40351                   break;
40352                 }
40353               }
40354             }
40355             return dialCode;
40356         },
40357         
40358         reset : function()
40359         {
40360             this.setValue(this.defaultDialCode);
40361             this.validate();
40362         },
40363         
40364         hiddenEl : function()
40365         {
40366             return this.el.select('input.hidden-tel-input',true).first();
40367         },
40368         
40369         onKeyUp : function(e){
40370             
40371             var k = e.getKey();
40372             var c = e.getCharCode();
40373             
40374             if(
40375                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40376                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40377             ){
40378                 e.stopEvent();
40379             }
40380             
40381             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40382             //     return;
40383             // }
40384             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40385                 e.stopEvent();
40386             }
40387             
40388             this.setValue(this.getValue());
40389         }
40390         
40391 });
40392 /**
40393  * @class Roo.bootstrap.MoneyField
40394  * @extends Roo.bootstrap.ComboBox
40395  * Bootstrap MoneyField class
40396  * 
40397  * @constructor
40398  * Create a new MoneyField.
40399  * @param {Object} config Configuration options
40400  */
40401
40402 Roo.bootstrap.MoneyField = function(config) {
40403     
40404     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40405     
40406 };
40407
40408 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40409     
40410     /**
40411      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40412      */
40413     allowDecimals : true,
40414     /**
40415      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40416      */
40417     decimalSeparator : ".",
40418     /**
40419      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40420      */
40421     decimalPrecision : 0,
40422     /**
40423      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40424      */
40425     allowNegative : true,
40426     /**
40427      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40428      */
40429     allowZero: true,
40430     /**
40431      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40432      */
40433     minValue : Number.NEGATIVE_INFINITY,
40434     /**
40435      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40436      */
40437     maxValue : Number.MAX_VALUE,
40438     /**
40439      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40440      */
40441     minText : "The minimum value for this field is {0}",
40442     /**
40443      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40444      */
40445     maxText : "The maximum value for this field is {0}",
40446     /**
40447      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40448      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40449      */
40450     nanText : "{0} is not a valid number",
40451     /**
40452      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40453      */
40454     castInt : true,
40455     /**
40456      * @cfg {String} defaults currency of the MoneyField
40457      * value should be in lkey
40458      */
40459     defaultCurrency : false,
40460     /**
40461      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40462      */
40463     thousandsDelimiter : false,
40464     
40465     
40466     inputlg : 9,
40467     inputmd : 9,
40468     inputsm : 9,
40469     inputxs : 6,
40470     
40471     store : false,
40472     
40473     getAutoCreate : function()
40474     {
40475         var align = this.labelAlign || this.parentLabelAlign();
40476         
40477         var id = Roo.id();
40478
40479         var cfg = {
40480             cls: 'form-group',
40481             cn: []
40482         };
40483
40484         var input =  {
40485             tag: 'input',
40486             id : id,
40487             cls : 'form-control roo-money-amount-input',
40488             autocomplete: 'new-password'
40489         };
40490         
40491         var hiddenInput = {
40492             tag: 'input',
40493             type: 'hidden',
40494             id: Roo.id(),
40495             cls: 'hidden-number-input'
40496         };
40497         
40498         if (this.name) {
40499             hiddenInput.name = this.name;
40500         }
40501
40502         if (this.disabled) {
40503             input.disabled = true;
40504         }
40505
40506         var clg = 12 - this.inputlg;
40507         var cmd = 12 - this.inputmd;
40508         var csm = 12 - this.inputsm;
40509         var cxs = 12 - this.inputxs;
40510         
40511         var container = {
40512             tag : 'div',
40513             cls : 'row roo-money-field',
40514             cn : [
40515                 {
40516                     tag : 'div',
40517                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40518                     cn : [
40519                         {
40520                             tag : 'div',
40521                             cls: 'roo-select2-container input-group',
40522                             cn: [
40523                                 {
40524                                     tag : 'input',
40525                                     cls : 'form-control roo-money-currency-input',
40526                                     autocomplete: 'new-password',
40527                                     readOnly : 1,
40528                                     name : this.currencyName
40529                                 },
40530                                 {
40531                                     tag :'span',
40532                                     cls : 'input-group-addon',
40533                                     cn : [
40534                                         {
40535                                             tag: 'span',
40536                                             cls: 'caret'
40537                                         }
40538                                     ]
40539                                 }
40540                             ]
40541                         }
40542                     ]
40543                 },
40544                 {
40545                     tag : 'div',
40546                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40547                     cn : [
40548                         {
40549                             tag: 'div',
40550                             cls: this.hasFeedback ? 'has-feedback' : '',
40551                             cn: [
40552                                 input
40553                             ]
40554                         }
40555                     ]
40556                 }
40557             ]
40558             
40559         };
40560         
40561         if (this.fieldLabel.length) {
40562             var indicator = {
40563                 tag: 'i',
40564                 tooltip: 'This field is required'
40565             };
40566
40567             var label = {
40568                 tag: 'label',
40569                 'for':  id,
40570                 cls: 'control-label',
40571                 cn: []
40572             };
40573
40574             var label_text = {
40575                 tag: 'span',
40576                 html: this.fieldLabel
40577             };
40578
40579             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40580             label.cn = [
40581                 indicator,
40582                 label_text
40583             ];
40584
40585             if(this.indicatorpos == 'right') {
40586                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40587                 label.cn = [
40588                     label_text,
40589                     indicator
40590                 ];
40591             }
40592
40593             if(align == 'left') {
40594                 container = {
40595                     tag: 'div',
40596                     cn: [
40597                         container
40598                     ]
40599                 };
40600
40601                 if(this.labelWidth > 12){
40602                     label.style = "width: " + this.labelWidth + 'px';
40603                 }
40604                 if(this.labelWidth < 13 && this.labelmd == 0){
40605                     this.labelmd = this.labelWidth;
40606                 }
40607                 if(this.labellg > 0){
40608                     label.cls += ' col-lg-' + this.labellg;
40609                     input.cls += ' col-lg-' + (12 - this.labellg);
40610                 }
40611                 if(this.labelmd > 0){
40612                     label.cls += ' col-md-' + this.labelmd;
40613                     container.cls += ' col-md-' + (12 - this.labelmd);
40614                 }
40615                 if(this.labelsm > 0){
40616                     label.cls += ' col-sm-' + this.labelsm;
40617                     container.cls += ' col-sm-' + (12 - this.labelsm);
40618                 }
40619                 if(this.labelxs > 0){
40620                     label.cls += ' col-xs-' + this.labelxs;
40621                     container.cls += ' col-xs-' + (12 - this.labelxs);
40622                 }
40623             }
40624         }
40625
40626         cfg.cn = [
40627             label,
40628             container,
40629             hiddenInput
40630         ];
40631         
40632         var settings = this;
40633
40634         ['xs','sm','md','lg'].map(function(size){
40635             if (settings[size]) {
40636                 cfg.cls += ' col-' + size + '-' + settings[size];
40637             }
40638         });
40639         
40640         return cfg;
40641     },
40642     
40643     initEvents : function()
40644     {
40645         this.indicator = this.indicatorEl();
40646         
40647         this.initCurrencyEvent();
40648         
40649         this.initNumberEvent();
40650     },
40651     
40652     initCurrencyEvent : function()
40653     {
40654         if (!this.store) {
40655             throw "can not find store for combo";
40656         }
40657         
40658         this.store = Roo.factory(this.store, Roo.data);
40659         this.store.parent = this;
40660         
40661         this.createList();
40662         
40663         this.triggerEl = this.el.select('.input-group-addon', true).first();
40664         
40665         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40666         
40667         var _this = this;
40668         
40669         (function(){
40670             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40671             _this.list.setWidth(lw);
40672         }).defer(100);
40673         
40674         this.list.on('mouseover', this.onViewOver, this);
40675         this.list.on('mousemove', this.onViewMove, this);
40676         this.list.on('scroll', this.onViewScroll, this);
40677         
40678         if(!this.tpl){
40679             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40680         }
40681         
40682         this.view = new Roo.View(this.list, this.tpl, {
40683             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40684         });
40685         
40686         this.view.on('click', this.onViewClick, this);
40687         
40688         this.store.on('beforeload', this.onBeforeLoad, this);
40689         this.store.on('load', this.onLoad, this);
40690         this.store.on('loadexception', this.onLoadException, this);
40691         
40692         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40693             "up" : function(e){
40694                 this.inKeyMode = true;
40695                 this.selectPrev();
40696             },
40697
40698             "down" : function(e){
40699                 if(!this.isExpanded()){
40700                     this.onTriggerClick();
40701                 }else{
40702                     this.inKeyMode = true;
40703                     this.selectNext();
40704                 }
40705             },
40706
40707             "enter" : function(e){
40708                 this.collapse();
40709                 
40710                 if(this.fireEvent("specialkey", this, e)){
40711                     this.onViewClick(false);
40712                 }
40713                 
40714                 return true;
40715             },
40716
40717             "esc" : function(e){
40718                 this.collapse();
40719             },
40720
40721             "tab" : function(e){
40722                 this.collapse();
40723                 
40724                 if(this.fireEvent("specialkey", this, e)){
40725                     this.onViewClick(false);
40726                 }
40727                 
40728                 return true;
40729             },
40730
40731             scope : this,
40732
40733             doRelay : function(foo, bar, hname){
40734                 if(hname == 'down' || this.scope.isExpanded()){
40735                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40736                 }
40737                 return true;
40738             },
40739
40740             forceKeyDown: true
40741         });
40742         
40743         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40744         
40745     },
40746     
40747     initNumberEvent : function(e)
40748     {
40749         this.inputEl().on("keydown" , this.fireKey,  this);
40750         this.inputEl().on("focus", this.onFocus,  this);
40751         this.inputEl().on("blur", this.onBlur,  this);
40752         
40753         this.inputEl().relayEvent('keyup', this);
40754         
40755         if(this.indicator){
40756             this.indicator.addClass('invisible');
40757         }
40758  
40759         this.originalValue = this.getValue();
40760         
40761         if(this.validationEvent == 'keyup'){
40762             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40763             this.inputEl().on('keyup', this.filterValidation, this);
40764         }
40765         else if(this.validationEvent !== false){
40766             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40767         }
40768         
40769         if(this.selectOnFocus){
40770             this.on("focus", this.preFocus, this);
40771             
40772         }
40773         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40774             this.inputEl().on("keypress", this.filterKeys, this);
40775         } else {
40776             this.inputEl().relayEvent('keypress', this);
40777         }
40778         
40779         var allowed = "0123456789";
40780         
40781         if(this.allowDecimals){
40782             allowed += this.decimalSeparator;
40783         }
40784         
40785         if(this.allowNegative){
40786             allowed += "-";
40787         }
40788         
40789         if(this.thousandsDelimiter) {
40790             allowed += ",";
40791         }
40792         
40793         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40794         
40795         var keyPress = function(e){
40796             
40797             var k = e.getKey();
40798             
40799             var c = e.getCharCode();
40800             
40801             if(
40802                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40803                     allowed.indexOf(String.fromCharCode(c)) === -1
40804             ){
40805                 e.stopEvent();
40806                 return;
40807             }
40808             
40809             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40810                 return;
40811             }
40812             
40813             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40814                 e.stopEvent();
40815             }
40816         };
40817         
40818         this.inputEl().on("keypress", keyPress, this);
40819         
40820     },
40821     
40822     onTriggerClick : function(e)
40823     {   
40824         if(this.disabled){
40825             return;
40826         }
40827         
40828         this.page = 0;
40829         this.loadNext = false;
40830         
40831         if(this.isExpanded()){
40832             this.collapse();
40833             return;
40834         }
40835         
40836         this.hasFocus = true;
40837         
40838         if(this.triggerAction == 'all') {
40839             this.doQuery(this.allQuery, true);
40840             return;
40841         }
40842         
40843         this.doQuery(this.getRawValue());
40844     },
40845     
40846     getCurrency : function()
40847     {   
40848         var v = this.currencyEl().getValue();
40849         
40850         return v;
40851     },
40852     
40853     restrictHeight : function()
40854     {
40855         this.list.alignTo(this.currencyEl(), this.listAlign);
40856         this.list.alignTo(this.currencyEl(), this.listAlign);
40857     },
40858     
40859     onViewClick : function(view, doFocus, el, e)
40860     {
40861         var index = this.view.getSelectedIndexes()[0];
40862         
40863         var r = this.store.getAt(index);
40864         
40865         if(r){
40866             this.onSelect(r, index);
40867         }
40868     },
40869     
40870     onSelect : function(record, index){
40871         
40872         if(this.fireEvent('beforeselect', this, record, index) !== false){
40873         
40874             this.setFromCurrencyData(index > -1 ? record.data : false);
40875             
40876             this.collapse();
40877             
40878             this.fireEvent('select', this, record, index);
40879         }
40880     },
40881     
40882     setFromCurrencyData : function(o)
40883     {
40884         var currency = '';
40885         
40886         this.lastCurrency = o;
40887         
40888         if (this.currencyField) {
40889             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40890         } else {
40891             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40892         }
40893         
40894         this.lastSelectionText = currency;
40895         
40896         //setting default currency
40897         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40898             this.setCurrency(this.defaultCurrency);
40899             return;
40900         }
40901         
40902         this.setCurrency(currency);
40903     },
40904     
40905     setFromData : function(o)
40906     {
40907         var c = {};
40908         
40909         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40910         
40911         this.setFromCurrencyData(c);
40912         
40913         var value = '';
40914         
40915         if (this.name) {
40916             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40917         } else {
40918             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40919         }
40920         
40921         this.setValue(value);
40922         
40923     },
40924     
40925     setCurrency : function(v)
40926     {   
40927         this.currencyValue = v;
40928         
40929         if(this.rendered){
40930             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40931             this.validate();
40932         }
40933     },
40934     
40935     setValue : function(v)
40936     {
40937         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40938         
40939         this.value = v;
40940         
40941         if(this.rendered){
40942             
40943             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40944             
40945             this.inputEl().dom.value = (v == '') ? '' :
40946                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40947             
40948             if(!this.allowZero && v === '0') {
40949                 this.hiddenEl().dom.value = '';
40950                 this.inputEl().dom.value = '';
40951             }
40952             
40953             this.validate();
40954         }
40955     },
40956     
40957     getRawValue : function()
40958     {
40959         var v = this.inputEl().getValue();
40960         
40961         return v;
40962     },
40963     
40964     getValue : function()
40965     {
40966         return this.fixPrecision(this.parseValue(this.getRawValue()));
40967     },
40968     
40969     parseValue : function(value)
40970     {
40971         if(this.thousandsDelimiter) {
40972             value += "";
40973             r = new RegExp(",", "g");
40974             value = value.replace(r, "");
40975         }
40976         
40977         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40978         return isNaN(value) ? '' : value;
40979         
40980     },
40981     
40982     fixPrecision : function(value)
40983     {
40984         if(this.thousandsDelimiter) {
40985             value += "";
40986             r = new RegExp(",", "g");
40987             value = value.replace(r, "");
40988         }
40989         
40990         var nan = isNaN(value);
40991         
40992         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40993             return nan ? '' : value;
40994         }
40995         return parseFloat(value).toFixed(this.decimalPrecision);
40996     },
40997     
40998     decimalPrecisionFcn : function(v)
40999     {
41000         return Math.floor(v);
41001     },
41002     
41003     validateValue : function(value)
41004     {
41005         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41006             return false;
41007         }
41008         
41009         var num = this.parseValue(value);
41010         
41011         if(isNaN(num)){
41012             this.markInvalid(String.format(this.nanText, value));
41013             return false;
41014         }
41015         
41016         if(num < this.minValue){
41017             this.markInvalid(String.format(this.minText, this.minValue));
41018             return false;
41019         }
41020         
41021         if(num > this.maxValue){
41022             this.markInvalid(String.format(this.maxText, this.maxValue));
41023             return false;
41024         }
41025         
41026         return true;
41027     },
41028     
41029     validate : function()
41030     {
41031         if(this.disabled || this.allowBlank){
41032             this.markValid();
41033             return true;
41034         }
41035         
41036         var currency = this.getCurrency();
41037         
41038         if(this.validateValue(this.getRawValue()) && currency.length){
41039             this.markValid();
41040             return true;
41041         }
41042         
41043         this.markInvalid();
41044         return false;
41045     },
41046     
41047     getName: function()
41048     {
41049         return this.name;
41050     },
41051     
41052     beforeBlur : function()
41053     {
41054         if(!this.castInt){
41055             return;
41056         }
41057         
41058         var v = this.parseValue(this.getRawValue());
41059         
41060         if(v || v == 0){
41061             this.setValue(v);
41062         }
41063     },
41064     
41065     onBlur : function()
41066     {
41067         this.beforeBlur();
41068         
41069         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41070             //this.el.removeClass(this.focusClass);
41071         }
41072         
41073         this.hasFocus = false;
41074         
41075         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41076             this.validate();
41077         }
41078         
41079         var v = this.getValue();
41080         
41081         if(String(v) !== String(this.startValue)){
41082             this.fireEvent('change', this, v, this.startValue);
41083         }
41084         
41085         this.fireEvent("blur", this);
41086     },
41087     
41088     inputEl : function()
41089     {
41090         return this.el.select('.roo-money-amount-input', true).first();
41091     },
41092     
41093     currencyEl : function()
41094     {
41095         return this.el.select('.roo-money-currency-input', true).first();
41096     },
41097     
41098     hiddenEl : function()
41099     {
41100         return this.el.select('input.hidden-number-input',true).first();
41101     }
41102     
41103 });