roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     
921     setWeight : function(str)
922     {
923         this.el.removeClass(this.weightClass);
924         this.el.addClass('btn-' + str);        
925     }
926     
927     
928 });
929
930  /*
931  * - LGPL
932  *
933  * column
934  * 
935  */
936
937 /**
938  * @class Roo.bootstrap.Column
939  * @extends Roo.bootstrap.Component
940  * Bootstrap Column class
941  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
942  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
943  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
944  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
945  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
946  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
947  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
948  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
949  *
950  * 
951  * @cfg {Boolean} hidden (true|false) hide the element
952  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
953  * @cfg {String} fa (ban|check|...) font awesome icon
954  * @cfg {Number} fasize (1|2|....) font awsome size
955
956  * @cfg {String} icon (info-sign|check|...) glyphicon name
957
958  * @cfg {String} html content of column.
959  * 
960  * @constructor
961  * Create a new Column
962  * @param {Object} config The config object
963  */
964
965 Roo.bootstrap.Column = function(config){
966     Roo.bootstrap.Column.superclass.constructor.call(this, config);
967 };
968
969 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
970     
971     xs: false,
972     sm: false,
973     md: false,
974     lg: false,
975     xsoff: false,
976     smoff: false,
977     mdoff: false,
978     lgoff: false,
979     html: '',
980     offset: 0,
981     alert: false,
982     fa: false,
983     icon : false,
984     hidden : false,
985     fasize : 1,
986     
987     getAutoCreate : function(){
988         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
989         
990         cfg = {
991             tag: 'div',
992             cls: 'column'
993         };
994         
995         var settings=this;
996         ['xs','sm','md','lg'].map(function(size){
997             //Roo.log( size + ':' + settings[size]);
998             
999             if (settings[size+'off'] !== false) {
1000                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1001             }
1002             
1003             if (settings[size] === false) {
1004                 return;
1005             }
1006             
1007             if (!settings[size]) { // 0 = hidden
1008                 cfg.cls += ' hidden-' + size;
1009                 return;
1010             }
1011             cfg.cls += ' col-' + size + '-' + settings[size];
1012             
1013         });
1014         
1015         if (this.hidden) {
1016             cfg.cls += ' hidden';
1017         }
1018         
1019         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1020             cfg.cls +=' alert alert-' + this.alert;
1021         }
1022         
1023         
1024         if (this.html.length) {
1025             cfg.html = this.html;
1026         }
1027         if (this.fa) {
1028             var fasize = '';
1029             if (this.fasize > 1) {
1030                 fasize = ' fa-' + this.fasize + 'x';
1031             }
1032             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1033             
1034             
1035         }
1036         if (this.icon) {
1037             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1038         }
1039         
1040         return cfg;
1041     }
1042    
1043 });
1044
1045  
1046
1047  /*
1048  * - LGPL
1049  *
1050  * page container.
1051  * 
1052  */
1053
1054
1055 /**
1056  * @class Roo.bootstrap.Container
1057  * @extends Roo.bootstrap.Component
1058  * Bootstrap Container class
1059  * @cfg {Boolean} jumbotron is it a jumbotron element
1060  * @cfg {String} html content of element
1061  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1062  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1063  * @cfg {String} header content of header (for panel)
1064  * @cfg {String} footer content of footer (for panel)
1065  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1066  * @cfg {String} tag (header|aside|section) type of HTML tag.
1067  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1068  * @cfg {String} fa font awesome icon
1069  * @cfg {String} icon (info-sign|check|...) glyphicon name
1070  * @cfg {Boolean} hidden (true|false) hide the element
1071  * @cfg {Boolean} expandable (true|false) default false
1072  * @cfg {Boolean} expanded (true|false) default true
1073  * @cfg {String} rheader contet on the right of header
1074  * @cfg {Boolean} clickable (true|false) default false
1075
1076  *     
1077  * @constructor
1078  * Create a new Container
1079  * @param {Object} config The config object
1080  */
1081
1082 Roo.bootstrap.Container = function(config){
1083     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1084     
1085     this.addEvents({
1086         // raw events
1087          /**
1088          * @event expand
1089          * After the panel has been expand
1090          * 
1091          * @param {Roo.bootstrap.Container} this
1092          */
1093         "expand" : true,
1094         /**
1095          * @event collapse
1096          * After the panel has been collapsed
1097          * 
1098          * @param {Roo.bootstrap.Container} this
1099          */
1100         "collapse" : true,
1101         /**
1102          * @event click
1103          * When a element is chick
1104          * @param {Roo.bootstrap.Container} this
1105          * @param {Roo.EventObject} e
1106          */
1107         "click" : true
1108     });
1109 };
1110
1111 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1112     
1113     jumbotron : false,
1114     well: '',
1115     panel : '',
1116     header: '',
1117     footer : '',
1118     sticky: '',
1119     tag : false,
1120     alert : false,
1121     fa: false,
1122     icon : false,
1123     expandable : false,
1124     rheader : '',
1125     expanded : true,
1126     clickable: false,
1127   
1128      
1129     getChildContainer : function() {
1130         
1131         if(!this.el){
1132             return false;
1133         }
1134         
1135         if (this.panel.length) {
1136             return this.el.select('.panel-body',true).first();
1137         }
1138         
1139         return this.el;
1140     },
1141     
1142     
1143     getAutoCreate : function(){
1144         
1145         var cfg = {
1146             tag : this.tag || 'div',
1147             html : '',
1148             cls : ''
1149         };
1150         if (this.jumbotron) {
1151             cfg.cls = 'jumbotron';
1152         }
1153         
1154         
1155         
1156         // - this is applied by the parent..
1157         //if (this.cls) {
1158         //    cfg.cls = this.cls + '';
1159         //}
1160         
1161         if (this.sticky.length) {
1162             
1163             var bd = Roo.get(document.body);
1164             if (!bd.hasClass('bootstrap-sticky')) {
1165                 bd.addClass('bootstrap-sticky');
1166                 Roo.select('html',true).setStyle('height', '100%');
1167             }
1168              
1169             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1170         }
1171         
1172         
1173         if (this.well.length) {
1174             switch (this.well) {
1175                 case 'lg':
1176                 case 'sm':
1177                     cfg.cls +=' well well-' +this.well;
1178                     break;
1179                 default:
1180                     cfg.cls +=' well';
1181                     break;
1182             }
1183         }
1184         
1185         if (this.hidden) {
1186             cfg.cls += ' hidden';
1187         }
1188         
1189         
1190         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1191             cfg.cls +=' alert alert-' + this.alert;
1192         }
1193         
1194         var body = cfg;
1195         
1196         if (this.panel.length) {
1197             cfg.cls += ' panel panel-' + this.panel;
1198             cfg.cn = [];
1199             if (this.header.length) {
1200                 
1201                 var h = [];
1202                 
1203                 if(this.expandable){
1204                     
1205                     cfg.cls = cfg.cls + ' expandable';
1206                     
1207                     h.push({
1208                         tag: 'i',
1209                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1210                     });
1211                     
1212                 }
1213                 
1214                 h.push(
1215                     {
1216                         tag: 'span',
1217                         cls : 'panel-title',
1218                         html : (this.expandable ? '&nbsp;' : '') + this.header
1219                     },
1220                     {
1221                         tag: 'span',
1222                         cls: 'panel-header-right',
1223                         html: this.rheader
1224                     }
1225                 );
1226                 
1227                 cfg.cn.push({
1228                     cls : 'panel-heading',
1229                     style : this.expandable ? 'cursor: pointer' : '',
1230                     cn : h
1231                 });
1232                 
1233             }
1234             
1235             body = false;
1236             cfg.cn.push({
1237                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1238                 html : this.html
1239             });
1240             
1241             
1242             if (this.footer.length) {
1243                 cfg.cn.push({
1244                     cls : 'panel-footer',
1245                     html : this.footer
1246                     
1247                 });
1248             }
1249             
1250         }
1251         
1252         if (body) {
1253             body.html = this.html || cfg.html;
1254             // prefix with the icons..
1255             if (this.fa) {
1256                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1257             }
1258             if (this.icon) {
1259                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1260             }
1261             
1262             
1263         }
1264         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1265             cfg.cls =  'container';
1266         }
1267         
1268         return cfg;
1269     },
1270     
1271     initEvents: function() 
1272     {
1273         if(this.expandable){
1274             var headerEl = this.headerEl();
1275         
1276             if(headerEl){
1277                 headerEl.on('click', this.onToggleClick, this);
1278             }
1279         }
1280         
1281         if(this.clickable){
1282             this.el.on('click', this.onClick, this);
1283         }
1284         
1285     },
1286     
1287     onToggleClick : function()
1288     {
1289         var headerEl = this.headerEl();
1290         
1291         if(!headerEl){
1292             return;
1293         }
1294         
1295         if(this.expanded){
1296             this.collapse();
1297             return;
1298         }
1299         
1300         this.expand();
1301     },
1302     
1303     expand : function()
1304     {
1305         if(this.fireEvent('expand', this)) {
1306             
1307             this.expanded = true;
1308             
1309             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1310             
1311             this.el.select('.panel-body',true).first().removeClass('hide');
1312             
1313             var toggleEl = this.toggleEl();
1314
1315             if(!toggleEl){
1316                 return;
1317             }
1318
1319             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1320         }
1321         
1322     },
1323     
1324     collapse : function()
1325     {
1326         if(this.fireEvent('collapse', this)) {
1327             
1328             this.expanded = false;
1329             
1330             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1331             this.el.select('.panel-body',true).first().addClass('hide');
1332         
1333             var toggleEl = this.toggleEl();
1334
1335             if(!toggleEl){
1336                 return;
1337             }
1338
1339             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1340         }
1341     },
1342     
1343     toggleEl : function()
1344     {
1345         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1346             return;
1347         }
1348         
1349         return this.el.select('.panel-heading .fa',true).first();
1350     },
1351     
1352     headerEl : function()
1353     {
1354         if(!this.el || !this.panel.length || !this.header.length){
1355             return;
1356         }
1357         
1358         return this.el.select('.panel-heading',true).first()
1359     },
1360     
1361     bodyEl : function()
1362     {
1363         if(!this.el || !this.panel.length){
1364             return;
1365         }
1366         
1367         return this.el.select('.panel-body',true).first()
1368     },
1369     
1370     titleEl : function()
1371     {
1372         if(!this.el || !this.panel.length || !this.header.length){
1373             return;
1374         }
1375         
1376         return this.el.select('.panel-title',true).first();
1377     },
1378     
1379     setTitle : function(v)
1380     {
1381         var titleEl = this.titleEl();
1382         
1383         if(!titleEl){
1384             return;
1385         }
1386         
1387         titleEl.dom.innerHTML = v;
1388     },
1389     
1390     getTitle : function()
1391     {
1392         
1393         var titleEl = this.titleEl();
1394         
1395         if(!titleEl){
1396             return '';
1397         }
1398         
1399         return titleEl.dom.innerHTML;
1400     },
1401     
1402     setRightTitle : function(v)
1403     {
1404         var t = this.el.select('.panel-header-right',true).first();
1405         
1406         if(!t){
1407             return;
1408         }
1409         
1410         t.dom.innerHTML = v;
1411     },
1412     
1413     onClick : function(e)
1414     {
1415         e.preventDefault();
1416         
1417         this.fireEvent('click', this, e);
1418     }
1419 });
1420
1421  /*
1422  * - LGPL
1423  *
1424  * image
1425  * 
1426  */
1427
1428
1429 /**
1430  * @class Roo.bootstrap.Img
1431  * @extends Roo.bootstrap.Component
1432  * Bootstrap Img class
1433  * @cfg {Boolean} imgResponsive false | true
1434  * @cfg {String} border rounded | circle | thumbnail
1435  * @cfg {String} src image source
1436  * @cfg {String} alt image alternative text
1437  * @cfg {String} href a tag href
1438  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1439  * @cfg {String} xsUrl xs image source
1440  * @cfg {String} smUrl sm image source
1441  * @cfg {String} mdUrl md image source
1442  * @cfg {String} lgUrl lg image source
1443  * 
1444  * @constructor
1445  * Create a new Input
1446  * @param {Object} config The config object
1447  */
1448
1449 Roo.bootstrap.Img = function(config){
1450     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1451     
1452     this.addEvents({
1453         // img events
1454         /**
1455          * @event click
1456          * The img click event for the img.
1457          * @param {Roo.EventObject} e
1458          */
1459         "click" : true
1460     });
1461 };
1462
1463 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1464     
1465     imgResponsive: true,
1466     border: '',
1467     src: 'about:blank',
1468     href: false,
1469     target: false,
1470     xsUrl: '',
1471     smUrl: '',
1472     mdUrl: '',
1473     lgUrl: '',
1474
1475     getAutoCreate : function()
1476     {   
1477         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1478             return this.createSingleImg();
1479         }
1480         
1481         var cfg = {
1482             tag: 'div',
1483             cls: 'roo-image-responsive-group',
1484             cn: []
1485         };
1486         var _this = this;
1487         
1488         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1489             
1490             if(!_this[size + 'Url']){
1491                 return;
1492             }
1493             
1494             var img = {
1495                 tag: 'img',
1496                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1497                 html: _this.html || cfg.html,
1498                 src: _this[size + 'Url']
1499             };
1500             
1501             img.cls += ' roo-image-responsive-' + size;
1502             
1503             var s = ['xs', 'sm', 'md', 'lg'];
1504             
1505             s.splice(s.indexOf(size), 1);
1506             
1507             Roo.each(s, function(ss){
1508                 img.cls += ' hidden-' + ss;
1509             });
1510             
1511             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1512                 cfg.cls += ' img-' + _this.border;
1513             }
1514             
1515             if(_this.alt){
1516                 cfg.alt = _this.alt;
1517             }
1518             
1519             if(_this.href){
1520                 var a = {
1521                     tag: 'a',
1522                     href: _this.href,
1523                     cn: [
1524                         img
1525                     ]
1526                 };
1527
1528                 if(this.target){
1529                     a.target = _this.target;
1530                 }
1531             }
1532             
1533             cfg.cn.push((_this.href) ? a : img);
1534             
1535         });
1536         
1537         return cfg;
1538     },
1539     
1540     createSingleImg : function()
1541     {
1542         var cfg = {
1543             tag: 'img',
1544             cls: (this.imgResponsive) ? 'img-responsive' : '',
1545             html : null,
1546             src : 'about:blank'  // just incase src get's set to undefined?!?
1547         };
1548         
1549         cfg.html = this.html || cfg.html;
1550         
1551         cfg.src = this.src || cfg.src;
1552         
1553         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1554             cfg.cls += ' img-' + this.border;
1555         }
1556         
1557         if(this.alt){
1558             cfg.alt = this.alt;
1559         }
1560         
1561         if(this.href){
1562             var a = {
1563                 tag: 'a',
1564                 href: this.href,
1565                 cn: [
1566                     cfg
1567                 ]
1568             };
1569             
1570             if(this.target){
1571                 a.target = this.target;
1572             }
1573             
1574         }
1575         
1576         return (this.href) ? a : cfg;
1577     },
1578     
1579     initEvents: function() 
1580     {
1581         if(!this.href){
1582             this.el.on('click', this.onClick, this);
1583         }
1584         
1585     },
1586     
1587     onClick : function(e)
1588     {
1589         Roo.log('img onclick');
1590         this.fireEvent('click', this, e);
1591     },
1592     /**
1593      * Sets the url of the image - used to update it
1594      * @param {String} url the url of the image
1595      */
1596     
1597     setSrc : function(url)
1598     {
1599         this.src =  url;
1600         
1601         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1602             this.el.dom.src =  url;
1603             return;
1604         }
1605         
1606         this.el.select('img', true).first().dom.src =  url;
1607     }
1608     
1609     
1610    
1611 });
1612
1613  /*
1614  * - LGPL
1615  *
1616  * image
1617  * 
1618  */
1619
1620
1621 /**
1622  * @class Roo.bootstrap.Link
1623  * @extends Roo.bootstrap.Component
1624  * Bootstrap Link Class
1625  * @cfg {String} alt image alternative text
1626  * @cfg {String} href a tag href
1627  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1628  * @cfg {String} html the content of the link.
1629  * @cfg {String} anchor name for the anchor link
1630  * @cfg {String} fa - favicon
1631
1632  * @cfg {Boolean} preventDefault (true | false) default false
1633
1634  * 
1635  * @constructor
1636  * Create a new Input
1637  * @param {Object} config The config object
1638  */
1639
1640 Roo.bootstrap.Link = function(config){
1641     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1642     
1643     this.addEvents({
1644         // img events
1645         /**
1646          * @event click
1647          * The img click event for the img.
1648          * @param {Roo.EventObject} e
1649          */
1650         "click" : true
1651     });
1652 };
1653
1654 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1655     
1656     href: false,
1657     target: false,
1658     preventDefault: false,
1659     anchor : false,
1660     alt : false,
1661     fa: false,
1662
1663
1664     getAutoCreate : function()
1665     {
1666         var html = this.html || '';
1667         
1668         if (this.fa !== false) {
1669             html = '<i class="fa fa-' + this.fa + '"></i>';
1670         }
1671         var cfg = {
1672             tag: 'a'
1673         };
1674         // anchor's do not require html/href...
1675         if (this.anchor === false) {
1676             cfg.html = html;
1677             cfg.href = this.href || '#';
1678         } else {
1679             cfg.name = this.anchor;
1680             if (this.html !== false || this.fa !== false) {
1681                 cfg.html = html;
1682             }
1683             if (this.href !== false) {
1684                 cfg.href = this.href;
1685             }
1686         }
1687         
1688         if(this.alt !== false){
1689             cfg.alt = this.alt;
1690         }
1691         
1692         
1693         if(this.target !== false) {
1694             cfg.target = this.target;
1695         }
1696         
1697         return cfg;
1698     },
1699     
1700     initEvents: function() {
1701         
1702         if(!this.href || this.preventDefault){
1703             this.el.on('click', this.onClick, this);
1704         }
1705     },
1706     
1707     onClick : function(e)
1708     {
1709         if(this.preventDefault){
1710             e.preventDefault();
1711         }
1712         //Roo.log('img onclick');
1713         this.fireEvent('click', this, e);
1714     }
1715    
1716 });
1717
1718  /*
1719  * - LGPL
1720  *
1721  * header
1722  * 
1723  */
1724
1725 /**
1726  * @class Roo.bootstrap.Header
1727  * @extends Roo.bootstrap.Component
1728  * Bootstrap Header class
1729  * @cfg {String} html content of header
1730  * @cfg {Number} level (1|2|3|4|5|6) default 1
1731  * 
1732  * @constructor
1733  * Create a new Header
1734  * @param {Object} config The config object
1735  */
1736
1737
1738 Roo.bootstrap.Header  = function(config){
1739     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1740 };
1741
1742 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1743     
1744     //href : false,
1745     html : false,
1746     level : 1,
1747     
1748     
1749     
1750     getAutoCreate : function(){
1751         
1752         
1753         
1754         var cfg = {
1755             tag: 'h' + (1 *this.level),
1756             html: this.html || ''
1757         } ;
1758         
1759         return cfg;
1760     }
1761    
1762 });
1763
1764  
1765
1766  /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776  
1777 /**
1778  * @class Roo.bootstrap.MenuMgr
1779  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1780  * @singleton
1781  */
1782 Roo.bootstrap.MenuMgr = function(){
1783    var menus, active, groups = {}, attached = false, lastShow = new Date();
1784
1785    // private - called when first menu is created
1786    function init(){
1787        menus = {};
1788        active = new Roo.util.MixedCollection();
1789        Roo.get(document).addKeyListener(27, function(){
1790            if(active.length > 0){
1791                hideAll();
1792            }
1793        });
1794    }
1795
1796    // private
1797    function hideAll(){
1798        if(active && active.length > 0){
1799            var c = active.clone();
1800            c.each(function(m){
1801                m.hide();
1802            });
1803        }
1804    }
1805
1806    // private
1807    function onHide(m){
1808        active.remove(m);
1809        if(active.length < 1){
1810            Roo.get(document).un("mouseup", onMouseDown);
1811             
1812            attached = false;
1813        }
1814    }
1815
1816    // private
1817    function onShow(m){
1818        var last = active.last();
1819        lastShow = new Date();
1820        active.add(m);
1821        if(!attached){
1822           Roo.get(document).on("mouseup", onMouseDown);
1823            
1824            attached = true;
1825        }
1826        if(m.parentMenu){
1827           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1828           m.parentMenu.activeChild = m;
1829        }else if(last && last.isVisible()){
1830           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1831        }
1832    }
1833
1834    // private
1835    function onBeforeHide(m){
1836        if(m.activeChild){
1837            m.activeChild.hide();
1838        }
1839        if(m.autoHideTimer){
1840            clearTimeout(m.autoHideTimer);
1841            delete m.autoHideTimer;
1842        }
1843    }
1844
1845    // private
1846    function onBeforeShow(m){
1847        var pm = m.parentMenu;
1848        if(!pm && !m.allowOtherMenus){
1849            hideAll();
1850        }else if(pm && pm.activeChild && active != m){
1851            pm.activeChild.hide();
1852        }
1853    }
1854
1855    // private this should really trigger on mouseup..
1856    function onMouseDown(e){
1857         Roo.log("on Mouse Up");
1858         
1859         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1860             Roo.log("MenuManager hideAll");
1861             hideAll();
1862             e.stopEvent();
1863         }
1864         
1865         
1866    }
1867
1868    // private
1869    function onBeforeCheck(mi, state){
1870        if(state){
1871            var g = groups[mi.group];
1872            for(var i = 0, l = g.length; i < l; i++){
1873                if(g[i] != mi){
1874                    g[i].setChecked(false);
1875                }
1876            }
1877        }
1878    }
1879
1880    return {
1881
1882        /**
1883         * Hides all menus that are currently visible
1884         */
1885        hideAll : function(){
1886             hideAll();  
1887        },
1888
1889        // private
1890        register : function(menu){
1891            if(!menus){
1892                init();
1893            }
1894            menus[menu.id] = menu;
1895            menu.on("beforehide", onBeforeHide);
1896            menu.on("hide", onHide);
1897            menu.on("beforeshow", onBeforeShow);
1898            menu.on("show", onShow);
1899            var g = menu.group;
1900            if(g && menu.events["checkchange"]){
1901                if(!groups[g]){
1902                    groups[g] = [];
1903                }
1904                groups[g].push(menu);
1905                menu.on("checkchange", onCheck);
1906            }
1907        },
1908
1909         /**
1910          * Returns a {@link Roo.menu.Menu} object
1911          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1912          * be used to generate and return a new Menu instance.
1913          */
1914        get : function(menu){
1915            if(typeof menu == "string"){ // menu id
1916                return menus[menu];
1917            }else if(menu.events){  // menu instance
1918                return menu;
1919            }
1920            /*else if(typeof menu.length == 'number'){ // array of menu items?
1921                return new Roo.bootstrap.Menu({items:menu});
1922            }else{ // otherwise, must be a config
1923                return new Roo.bootstrap.Menu(menu);
1924            }
1925            */
1926            return false;
1927        },
1928
1929        // private
1930        unregister : function(menu){
1931            delete menus[menu.id];
1932            menu.un("beforehide", onBeforeHide);
1933            menu.un("hide", onHide);
1934            menu.un("beforeshow", onBeforeShow);
1935            menu.un("show", onShow);
1936            var g = menu.group;
1937            if(g && menu.events["checkchange"]){
1938                groups[g].remove(menu);
1939                menu.un("checkchange", onCheck);
1940            }
1941        },
1942
1943        // private
1944        registerCheckable : function(menuItem){
1945            var g = menuItem.group;
1946            if(g){
1947                if(!groups[g]){
1948                    groups[g] = [];
1949                }
1950                groups[g].push(menuItem);
1951                menuItem.on("beforecheckchange", onBeforeCheck);
1952            }
1953        },
1954
1955        // private
1956        unregisterCheckable : function(menuItem){
1957            var g = menuItem.group;
1958            if(g){
1959                groups[g].remove(menuItem);
1960                menuItem.un("beforecheckchange", onBeforeCheck);
1961            }
1962        }
1963    };
1964 }();/*
1965  * - LGPL
1966  *
1967  * menu
1968  * 
1969  */
1970
1971 /**
1972  * @class Roo.bootstrap.Menu
1973  * @extends Roo.bootstrap.Component
1974  * Bootstrap Menu class - container for MenuItems
1975  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1976  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1977  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1978  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1979  * 
1980  * @constructor
1981  * Create a new Menu
1982  * @param {Object} config The config object
1983  */
1984
1985
1986 Roo.bootstrap.Menu = function(config){
1987     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1988     if (this.registerMenu && this.type != 'treeview')  {
1989         Roo.bootstrap.MenuMgr.register(this);
1990     }
1991     this.addEvents({
1992         /**
1993          * @event beforeshow
1994          * Fires before this menu is displayed
1995          * @param {Roo.menu.Menu} this
1996          */
1997         beforeshow : true,
1998         /**
1999          * @event beforehide
2000          * Fires before this menu is hidden
2001          * @param {Roo.menu.Menu} this
2002          */
2003         beforehide : true,
2004         /**
2005          * @event show
2006          * Fires after this menu is displayed
2007          * @param {Roo.menu.Menu} this
2008          */
2009         show : true,
2010         /**
2011          * @event hide
2012          * Fires after this menu is hidden
2013          * @param {Roo.menu.Menu} this
2014          */
2015         hide : true,
2016         /**
2017          * @event click
2018          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2019          * @param {Roo.menu.Menu} this
2020          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2021          * @param {Roo.EventObject} e
2022          */
2023         click : true,
2024         /**
2025          * @event mouseover
2026          * Fires when the mouse is hovering over this menu
2027          * @param {Roo.menu.Menu} this
2028          * @param {Roo.EventObject} e
2029          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2030          */
2031         mouseover : true,
2032         /**
2033          * @event mouseout
2034          * Fires when the mouse exits this menu
2035          * @param {Roo.menu.Menu} this
2036          * @param {Roo.EventObject} e
2037          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038          */
2039         mouseout : true,
2040         /**
2041          * @event itemclick
2042          * Fires when a menu item contained in this menu is clicked
2043          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2044          * @param {Roo.EventObject} e
2045          */
2046         itemclick: true
2047     });
2048     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2049 };
2050
2051 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2052     
2053    /// html : false,
2054     //align : '',
2055     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2056     type: false,
2057     /**
2058      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2059      */
2060     registerMenu : true,
2061     
2062     menuItems :false, // stores the menu items..
2063     
2064     hidden:true,
2065         
2066     parentMenu : false,
2067     
2068     stopEvent : true,
2069     
2070     isLink : false,
2071     
2072     getChildContainer : function() {
2073         return this.el;  
2074     },
2075     
2076     getAutoCreate : function(){
2077          
2078         //if (['right'].indexOf(this.align)!==-1) {
2079         //    cfg.cn[1].cls += ' pull-right'
2080         //}
2081         
2082         
2083         var cfg = {
2084             tag : 'ul',
2085             cls : 'dropdown-menu' ,
2086             style : 'z-index:1000'
2087             
2088         };
2089         
2090         if (this.type === 'submenu') {
2091             cfg.cls = 'submenu active';
2092         }
2093         if (this.type === 'treeview') {
2094             cfg.cls = 'treeview-menu';
2095         }
2096         
2097         return cfg;
2098     },
2099     initEvents : function() {
2100         
2101        // Roo.log("ADD event");
2102        // Roo.log(this.triggerEl.dom);
2103         
2104         this.triggerEl.on('click', this.onTriggerClick, this);
2105         
2106         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2107         
2108         this.triggerEl.addClass('dropdown-toggle');
2109         
2110         if (Roo.isTouch) {
2111             this.el.on('touchstart'  , this.onTouch, this);
2112         }
2113         this.el.on('click' , this.onClick, this);
2114
2115         this.el.on("mouseover", this.onMouseOver, this);
2116         this.el.on("mouseout", this.onMouseOut, this);
2117         
2118     },
2119     
2120     findTargetItem : function(e)
2121     {
2122         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2123         if(!t){
2124             return false;
2125         }
2126         //Roo.log(t);         Roo.log(t.id);
2127         if(t && t.id){
2128             //Roo.log(this.menuitems);
2129             return this.menuitems.get(t.id);
2130             
2131             //return this.items.get(t.menuItemId);
2132         }
2133         
2134         return false;
2135     },
2136     
2137     onTouch : function(e) 
2138     {
2139         Roo.log("menu.onTouch");
2140         //e.stopEvent(); this make the user popdown broken
2141         this.onClick(e);
2142     },
2143     
2144     onClick : function(e)
2145     {
2146         Roo.log("menu.onClick");
2147         
2148         var t = this.findTargetItem(e);
2149         if(!t || t.isContainer){
2150             return;
2151         }
2152         Roo.log(e);
2153         /*
2154         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2155             if(t == this.activeItem && t.shouldDeactivate(e)){
2156                 this.activeItem.deactivate();
2157                 delete this.activeItem;
2158                 return;
2159             }
2160             if(t.canActivate){
2161                 this.setActiveItem(t, true);
2162             }
2163             return;
2164             
2165             
2166         }
2167         */
2168        
2169         Roo.log('pass click event');
2170         
2171         t.onClick(e);
2172         
2173         this.fireEvent("click", this, t, e);
2174         
2175         var _this = this;
2176         
2177         if(!t.href.length || t.href == '#'){
2178             (function() { _this.hide(); }).defer(100);
2179         }
2180         
2181     },
2182     
2183     onMouseOver : function(e){
2184         var t  = this.findTargetItem(e);
2185         //Roo.log(t);
2186         //if(t){
2187         //    if(t.canActivate && !t.disabled){
2188         //        this.setActiveItem(t, true);
2189         //    }
2190         //}
2191         
2192         this.fireEvent("mouseover", this, e, t);
2193     },
2194     isVisible : function(){
2195         return !this.hidden;
2196     },
2197      onMouseOut : function(e){
2198         var t  = this.findTargetItem(e);
2199         
2200         //if(t ){
2201         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2202         //        this.activeItem.deactivate();
2203         //        delete this.activeItem;
2204         //    }
2205         //}
2206         this.fireEvent("mouseout", this, e, t);
2207     },
2208     
2209     
2210     /**
2211      * Displays this menu relative to another element
2212      * @param {String/HTMLElement/Roo.Element} element The element to align to
2213      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2214      * the element (defaults to this.defaultAlign)
2215      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2216      */
2217     show : function(el, pos, parentMenu){
2218         this.parentMenu = parentMenu;
2219         if(!this.el){
2220             this.render();
2221         }
2222         this.fireEvent("beforeshow", this);
2223         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2224     },
2225      /**
2226      * Displays this menu at a specific xy position
2227      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2228      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2229      */
2230     showAt : function(xy, parentMenu, /* private: */_e){
2231         this.parentMenu = parentMenu;
2232         if(!this.el){
2233             this.render();
2234         }
2235         if(_e !== false){
2236             this.fireEvent("beforeshow", this);
2237             //xy = this.el.adjustForConstraints(xy);
2238         }
2239         
2240         //this.el.show();
2241         this.hideMenuItems();
2242         this.hidden = false;
2243         this.triggerEl.addClass('open');
2244         
2245         // reassign x when hitting right
2246         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2247             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2248         }
2249         
2250         // reassign y when hitting bottom
2251         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2252             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2253         }
2254         
2255         // but the list may align on trigger left or trigger top... should it be a properity?
2256         
2257         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2258             this.el.setXY(xy);
2259         }
2260         
2261         this.focus();
2262         this.fireEvent("show", this);
2263     },
2264     
2265     focus : function(){
2266         return;
2267         if(!this.hidden){
2268             this.doFocus.defer(50, this);
2269         }
2270     },
2271
2272     doFocus : function(){
2273         if(!this.hidden){
2274             this.focusEl.focus();
2275         }
2276     },
2277
2278     /**
2279      * Hides this menu and optionally all parent menus
2280      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2281      */
2282     hide : function(deep)
2283     {
2284         
2285         this.hideMenuItems();
2286         if(this.el && this.isVisible()){
2287             this.fireEvent("beforehide", this);
2288             if(this.activeItem){
2289                 this.activeItem.deactivate();
2290                 this.activeItem = null;
2291             }
2292             this.triggerEl.removeClass('open');;
2293             this.hidden = true;
2294             this.fireEvent("hide", this);
2295         }
2296         if(deep === true && this.parentMenu){
2297             this.parentMenu.hide(true);
2298         }
2299     },
2300     
2301     onTriggerClick : function(e)
2302     {
2303         Roo.log('trigger click');
2304         
2305         var target = e.getTarget();
2306         
2307         Roo.log(target.nodeName.toLowerCase());
2308         
2309         if(target.nodeName.toLowerCase() === 'i'){
2310             e.preventDefault();
2311         }
2312         
2313     },
2314     
2315     onTriggerPress  : function(e)
2316     {
2317         Roo.log('trigger press');
2318         //Roo.log(e.getTarget());
2319        // Roo.log(this.triggerEl.dom);
2320        
2321         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2322         var pel = Roo.get(e.getTarget());
2323         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2324             Roo.log('is treeview or dropdown?');
2325             return;
2326         }
2327         
2328         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2329             return;
2330         }
2331         
2332         if (this.isVisible()) {
2333             Roo.log('hide');
2334             this.hide();
2335         } else {
2336             Roo.log('show');
2337             this.show(this.triggerEl, false, false);
2338         }
2339         
2340         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2341             e.stopEvent();
2342         }
2343         
2344     },
2345        
2346     
2347     hideMenuItems : function()
2348     {
2349         Roo.log("hide Menu Items");
2350         if (!this.el) { 
2351             return;
2352         }
2353         //$(backdrop).remove()
2354         this.el.select('.open',true).each(function(aa) {
2355             
2356             aa.removeClass('open');
2357           //var parent = getParent($(this))
2358           //var relatedTarget = { relatedTarget: this }
2359           
2360            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2361           //if (e.isDefaultPrevented()) return
2362            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2363         });
2364     },
2365     addxtypeChild : function (tree, cntr) {
2366         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2367           
2368         this.menuitems.add(comp);
2369         return comp;
2370
2371     },
2372     getEl : function()
2373     {
2374         Roo.log(this.el);
2375         return this.el;
2376     },
2377     
2378     clear : function()
2379     {
2380         this.getEl().dom.innerHTML = '';
2381         this.menuitems.clear();
2382     }
2383 });
2384
2385  
2386  /*
2387  * - LGPL
2388  *
2389  * menu item
2390  * 
2391  */
2392
2393
2394 /**
2395  * @class Roo.bootstrap.MenuItem
2396  * @extends Roo.bootstrap.Component
2397  * Bootstrap MenuItem class
2398  * @cfg {String} html the menu label
2399  * @cfg {String} href the link
2400  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2401  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2402  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2403  * @cfg {String} fa favicon to show on left of menu item.
2404  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2405  * 
2406  * 
2407  * @constructor
2408  * Create a new MenuItem
2409  * @param {Object} config The config object
2410  */
2411
2412
2413 Roo.bootstrap.MenuItem = function(config){
2414     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2415     this.addEvents({
2416         // raw events
2417         /**
2418          * @event click
2419          * The raw click event for the entire grid.
2420          * @param {Roo.bootstrap.MenuItem} this
2421          * @param {Roo.EventObject} e
2422          */
2423         "click" : true
2424     });
2425 };
2426
2427 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2428     
2429     href : false,
2430     html : false,
2431     preventDefault: false,
2432     isContainer : false,
2433     active : false,
2434     fa: false,
2435     
2436     getAutoCreate : function(){
2437         
2438         if(this.isContainer){
2439             return {
2440                 tag: 'li',
2441                 cls: 'dropdown-menu-item'
2442             };
2443         }
2444         var ctag = {
2445             tag: 'span',
2446             html: 'Link'
2447         };
2448         
2449         var anc = {
2450             tag : 'a',
2451             href : '#',
2452             cn : [  ]
2453         };
2454         
2455         if (this.fa !== false) {
2456             anc.cn.push({
2457                 tag : 'i',
2458                 cls : 'fa fa-' + this.fa
2459             });
2460         }
2461         
2462         anc.cn.push(ctag);
2463         
2464         
2465         var cfg= {
2466             tag: 'li',
2467             cls: 'dropdown-menu-item',
2468             cn: [ anc ]
2469         };
2470         if (this.parent().type == 'treeview') {
2471             cfg.cls = 'treeview-menu';
2472         }
2473         if (this.active) {
2474             cfg.cls += ' active';
2475         }
2476         
2477         
2478         
2479         anc.href = this.href || cfg.cn[0].href ;
2480         ctag.html = this.html || cfg.cn[0].html ;
2481         return cfg;
2482     },
2483     
2484     initEvents: function()
2485     {
2486         if (this.parent().type == 'treeview') {
2487             this.el.select('a').on('click', this.onClick, this);
2488         }
2489         
2490         if (this.menu) {
2491             this.menu.parentType = this.xtype;
2492             this.menu.triggerEl = this.el;
2493             this.menu = this.addxtype(Roo.apply({}, this.menu));
2494         }
2495         
2496     },
2497     onClick : function(e)
2498     {
2499         Roo.log('item on click ');
2500         
2501         if(this.preventDefault){
2502             e.preventDefault();
2503         }
2504         //this.parent().hideMenuItems();
2505         
2506         this.fireEvent('click', this, e);
2507     },
2508     getEl : function()
2509     {
2510         return this.el;
2511     } 
2512 });
2513
2514  
2515
2516  /*
2517  * - LGPL
2518  *
2519  * menu separator
2520  * 
2521  */
2522
2523
2524 /**
2525  * @class Roo.bootstrap.MenuSeparator
2526  * @extends Roo.bootstrap.Component
2527  * Bootstrap MenuSeparator class
2528  * 
2529  * @constructor
2530  * Create a new MenuItem
2531  * @param {Object} config The config object
2532  */
2533
2534
2535 Roo.bootstrap.MenuSeparator = function(config){
2536     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2537 };
2538
2539 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2540     
2541     getAutoCreate : function(){
2542         var cfg = {
2543             cls: 'divider',
2544             tag : 'li'
2545         };
2546         
2547         return cfg;
2548     }
2549    
2550 });
2551
2552  
2553
2554  
2555 /*
2556 * Licence: LGPL
2557 */
2558
2559 /**
2560  * @class Roo.bootstrap.Modal
2561  * @extends Roo.bootstrap.Component
2562  * Bootstrap Modal class
2563  * @cfg {String} title Title of dialog
2564  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2565  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2566  * @cfg {Boolean} specificTitle default false
2567  * @cfg {Array} buttons Array of buttons or standard button set..
2568  * @cfg {String} buttonPosition (left|right|center) default right
2569  * @cfg {Boolean} animate default true
2570  * @cfg {Boolean} allow_close default true
2571  * @cfg {Boolean} fitwindow default false
2572  * @cfg {String} size (sm|lg) default empty
2573  * @cfg {Number} max_width set the max width of modal
2574  *
2575  *
2576  * @constructor
2577  * Create a new Modal Dialog
2578  * @param {Object} config The config object
2579  */
2580
2581 Roo.bootstrap.Modal = function(config){
2582     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2583     this.addEvents({
2584         // raw events
2585         /**
2586          * @event btnclick
2587          * The raw btnclick event for the button
2588          * @param {Roo.EventObject} e
2589          */
2590         "btnclick" : true,
2591         /**
2592          * @event resize
2593          * Fire when dialog resize
2594          * @param {Roo.bootstrap.Modal} this
2595          * @param {Roo.EventObject} e
2596          */
2597         "resize" : true
2598     });
2599     this.buttons = this.buttons || [];
2600
2601     if (this.tmpl) {
2602         this.tmpl = Roo.factory(this.tmpl);
2603     }
2604
2605 };
2606
2607 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2608
2609     title : 'test dialog',
2610
2611     buttons : false,
2612
2613     // set on load...
2614
2615     html: false,
2616
2617     tmp: false,
2618
2619     specificTitle: false,
2620
2621     buttonPosition: 'right',
2622
2623     allow_close : true,
2624
2625     animate : true,
2626
2627     fitwindow: false,
2628     
2629      // private
2630     dialogEl: false,
2631     bodyEl:  false,
2632     footerEl:  false,
2633     titleEl:  false,
2634     closeEl:  false,
2635
2636     size: '',
2637     
2638     max_width: 0,
2639     
2640     max_height: 0,
2641     
2642     fit_content: false,
2643
2644     onRender : function(ct, position)
2645     {
2646         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2647
2648         if(!this.el){
2649             var cfg = Roo.apply({},  this.getAutoCreate());
2650             cfg.id = Roo.id();
2651             //if(!cfg.name){
2652             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2653             //}
2654             //if (!cfg.name.length) {
2655             //    delete cfg.name;
2656            // }
2657             if (this.cls) {
2658                 cfg.cls += ' ' + this.cls;
2659             }
2660             if (this.style) {
2661                 cfg.style = this.style;
2662             }
2663             this.el = Roo.get(document.body).createChild(cfg, position);
2664         }
2665         //var type = this.el.dom.type;
2666
2667
2668         if(this.tabIndex !== undefined){
2669             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2670         }
2671
2672         this.dialogEl = this.el.select('.modal-dialog',true).first();
2673         this.bodyEl = this.el.select('.modal-body',true).first();
2674         this.closeEl = this.el.select('.modal-header .close', true).first();
2675         this.headerEl = this.el.select('.modal-header',true).first();
2676         this.titleEl = this.el.select('.modal-title',true).first();
2677         this.footerEl = this.el.select('.modal-footer',true).first();
2678
2679         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2680         
2681         //this.el.addClass("x-dlg-modal");
2682
2683         if (this.buttons.length) {
2684             Roo.each(this.buttons, function(bb) {
2685                 var b = Roo.apply({}, bb);
2686                 b.xns = b.xns || Roo.bootstrap;
2687                 b.xtype = b.xtype || 'Button';
2688                 if (typeof(b.listeners) == 'undefined') {
2689                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2690                 }
2691
2692                 var btn = Roo.factory(b);
2693
2694                 btn.render(this.el.select('.modal-footer div').first());
2695
2696             },this);
2697         }
2698         // render the children.
2699         var nitems = [];
2700
2701         if(typeof(this.items) != 'undefined'){
2702             var items = this.items;
2703             delete this.items;
2704
2705             for(var i =0;i < items.length;i++) {
2706                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2707             }
2708         }
2709
2710         this.items = nitems;
2711
2712         // where are these used - they used to be body/close/footer
2713
2714
2715         this.initEvents();
2716         //this.el.addClass([this.fieldClass, this.cls]);
2717
2718     },
2719
2720     getAutoCreate : function()
2721     {
2722         var bdy = {
2723                 cls : 'modal-body',
2724                 html : this.html || ''
2725         };
2726
2727         var title = {
2728             tag: 'h4',
2729             cls : 'modal-title',
2730             html : this.title
2731         };
2732
2733         if(this.specificTitle){
2734             title = this.title;
2735
2736         };
2737
2738         var header = [];
2739         if (this.allow_close) {
2740             header.push({
2741                 tag: 'button',
2742                 cls : 'close',
2743                 html : '&times'
2744             });
2745         }
2746
2747         header.push(title);
2748
2749         var size = '';
2750
2751         if(this.size.length){
2752             size = 'modal-' + this.size;
2753         }
2754
2755         var modal = {
2756             cls: "modal",
2757              cn : [
2758                 {
2759                     cls: "modal-dialog " + size,
2760                     cn : [
2761                         {
2762                             cls : "modal-content",
2763                             cn : [
2764                                 {
2765                                     cls : 'modal-header',
2766                                     cn : header
2767                                 },
2768                                 bdy,
2769                                 {
2770                                     cls : 'modal-footer',
2771                                     cn : [
2772                                         {
2773                                             tag: 'div',
2774                                             cls: 'btn-' + this.buttonPosition
2775                                         }
2776                                     ]
2777
2778                                 }
2779
2780
2781                             ]
2782
2783                         }
2784                     ]
2785
2786                 }
2787             ]
2788         };
2789
2790         if(this.animate){
2791             modal.cls += ' fade';
2792         }
2793
2794         return modal;
2795
2796     },
2797     getChildContainer : function() {
2798
2799          return this.bodyEl;
2800
2801     },
2802     getButtonContainer : function() {
2803          return this.el.select('.modal-footer div',true).first();
2804
2805     },
2806     initEvents : function()
2807     {
2808         if (this.allow_close) {
2809             this.closeEl.on('click', this.hide, this);
2810         }
2811         Roo.EventManager.onWindowResize(this.resize, this, true);
2812
2813
2814     },
2815
2816     resize : function()
2817     {
2818         this.maskEl.setSize(
2819             Roo.lib.Dom.getViewWidth(true),
2820             Roo.lib.Dom.getViewHeight(true)
2821         );
2822         
2823         if (this.fitwindow) {
2824             this.setSize(
2825                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2826                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2827             );
2828             return;
2829         }
2830         
2831         if(this.max_width !== 0) {
2832             
2833             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2834             
2835             if(this.height) {
2836                 this.setSize(w, this.height);
2837                 return;
2838             }
2839             
2840             if(this.max_height) {
2841                 this.setSize(w,Math.min(
2842                     this.max_height,
2843                     Roo.lib.Dom.getViewportHeight(true) - 60
2844                 ));
2845                 
2846                 return;
2847             }
2848             
2849             if(!this.fit_content) {
2850                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2851                 return;
2852             }
2853             
2854             this.setSize(w, Math.min(
2855                 60 +
2856                 this.headerEl.getHeight() + 
2857                 this.footerEl.getHeight() + 
2858                 this.getChildHeight(this.bodyEl.dom.childNodes),
2859                 Roo.lib.Dom.getViewportHeight(true) - 60)
2860             );
2861         }
2862         
2863     },
2864
2865     setSize : function(w,h)
2866     {
2867         if (!w && !h) {
2868             return;
2869         }
2870         
2871         this.resizeTo(w,h);
2872     },
2873
2874     show : function() {
2875
2876         if (!this.rendered) {
2877             this.render();
2878         }
2879
2880         //this.el.setStyle('display', 'block');
2881         this.el.removeClass('hideing');        
2882         this.el.addClass('show');
2883  
2884         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2885             var _this = this;
2886             (function(){
2887                 this.el.addClass('in');
2888             }).defer(50, this);
2889         }else{
2890             this.el.addClass('in');
2891         }
2892
2893         // not sure how we can show data in here..
2894         //if (this.tmpl) {
2895         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2896         //}
2897
2898         Roo.get(document.body).addClass("x-body-masked");
2899         
2900         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2901         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2902         this.maskEl.addClass('show');
2903         
2904         this.resize();
2905         
2906         this.fireEvent('show', this);
2907
2908         // set zindex here - otherwise it appears to be ignored...
2909         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2910
2911         (function () {
2912             this.items.forEach( function(e) {
2913                 e.layout ? e.layout() : false;
2914
2915             });
2916         }).defer(100,this);
2917
2918     },
2919     hide : function()
2920     {
2921         if(this.fireEvent("beforehide", this) !== false){
2922             this.maskEl.removeClass('show');
2923             Roo.get(document.body).removeClass("x-body-masked");
2924             this.el.removeClass('in');
2925             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2926
2927             if(this.animate){ // why
2928                 this.el.addClass('hideing');
2929                 (function(){
2930                     if (!this.el.hasClass('hideing')) {
2931                         return; // it's been shown again...
2932                     }
2933                     this.el.removeClass('show');
2934                     this.el.removeClass('hideing');
2935                 }).defer(150,this);
2936                 
2937             }else{
2938                  this.el.removeClass('show');
2939             }
2940             this.fireEvent('hide', this);
2941         }
2942     },
2943     isVisible : function()
2944     {
2945         
2946         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2947         
2948     },
2949
2950     addButton : function(str, cb)
2951     {
2952
2953
2954         var b = Roo.apply({}, { html : str } );
2955         b.xns = b.xns || Roo.bootstrap;
2956         b.xtype = b.xtype || 'Button';
2957         if (typeof(b.listeners) == 'undefined') {
2958             b.listeners = { click : cb.createDelegate(this)  };
2959         }
2960
2961         var btn = Roo.factory(b);
2962
2963         btn.render(this.el.select('.modal-footer div').first());
2964
2965         return btn;
2966
2967     },
2968
2969     setDefaultButton : function(btn)
2970     {
2971         //this.el.select('.modal-footer').()
2972     },
2973     diff : false,
2974
2975     resizeTo: function(w,h)
2976     {
2977         // skip.. ?? why??
2978
2979         this.dialogEl.setWidth(w);
2980         if (this.diff === false) {
2981             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2982         }
2983
2984         this.bodyEl.setHeight(h - this.diff);
2985
2986         this.fireEvent('resize', this);
2987
2988     },
2989     setContentSize  : function(w, h)
2990     {
2991
2992     },
2993     onButtonClick: function(btn,e)
2994     {
2995         //Roo.log([a,b,c]);
2996         this.fireEvent('btnclick', btn.name, e);
2997     },
2998      /**
2999      * Set the title of the Dialog
3000      * @param {String} str new Title
3001      */
3002     setTitle: function(str) {
3003         this.titleEl.dom.innerHTML = str;
3004     },
3005     /**
3006      * Set the body of the Dialog
3007      * @param {String} str new Title
3008      */
3009     setBody: function(str) {
3010         this.bodyEl.dom.innerHTML = str;
3011     },
3012     /**
3013      * Set the body of the Dialog using the template
3014      * @param {Obj} data - apply this data to the template and replace the body contents.
3015      */
3016     applyBody: function(obj)
3017     {
3018         if (!this.tmpl) {
3019             Roo.log("Error - using apply Body without a template");
3020             //code
3021         }
3022         this.tmpl.overwrite(this.bodyEl, obj);
3023     },
3024     
3025     getChildHeight : function(child_nodes)
3026     {
3027         if(
3028             !child_nodes ||
3029             child_nodes.length == 0
3030         ) {
3031             return;
3032         }
3033         
3034         var child_height = 0;
3035         
3036         for(var i = 0; i < child_nodes.length; i++) {
3037             
3038             /*
3039             * for modal with tabs...
3040             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3041                 
3042                 var layout_childs = child_nodes[i].childNodes;
3043                 
3044                 for(var j = 0; j < layout_childs.length; j++) {
3045                     
3046                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3047                         
3048                         var layout_body_childs = layout_childs[j].childNodes;
3049                         
3050                         for(var k = 0; k < layout_body_childs.length; k++) {
3051                             
3052                             if(layout_body_childs[k].classList.contains('navbar')) {
3053                                 child_height += layout_body_childs[k].offsetHeight;
3054                                 continue;
3055                             }
3056                             
3057                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3058                                 
3059                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3060                                 
3061                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3062                                     
3063                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3064                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3065                                         continue;
3066                                     }
3067                                     
3068                                 }
3069                                 
3070                             }
3071                             
3072                         }
3073                     }
3074                 }
3075                 continue;
3076             }
3077             */
3078             
3079             child_height += child_nodes[i].offsetHeight;
3080             // Roo.log(child_nodes[i].offsetHeight);
3081         }
3082         
3083         return child_height;
3084     }
3085
3086 });
3087
3088
3089 Roo.apply(Roo.bootstrap.Modal,  {
3090     /**
3091          * Button config that displays a single OK button
3092          * @type Object
3093          */
3094         OK :  [{
3095             name : 'ok',
3096             weight : 'primary',
3097             html : 'OK'
3098         }],
3099         /**
3100          * Button config that displays Yes and No buttons
3101          * @type Object
3102          */
3103         YESNO : [
3104             {
3105                 name  : 'no',
3106                 html : 'No'
3107             },
3108             {
3109                 name  :'yes',
3110                 weight : 'primary',
3111                 html : 'Yes'
3112             }
3113         ],
3114
3115         /**
3116          * Button config that displays OK and Cancel buttons
3117          * @type Object
3118          */
3119         OKCANCEL : [
3120             {
3121                name : 'cancel',
3122                 html : 'Cancel'
3123             },
3124             {
3125                 name : 'ok',
3126                 weight : 'primary',
3127                 html : 'OK'
3128             }
3129         ],
3130         /**
3131          * Button config that displays Yes, No and Cancel buttons
3132          * @type Object
3133          */
3134         YESNOCANCEL : [
3135             {
3136                 name : 'yes',
3137                 weight : 'primary',
3138                 html : 'Yes'
3139             },
3140             {
3141                 name : 'no',
3142                 html : 'No'
3143             },
3144             {
3145                 name : 'cancel',
3146                 html : 'Cancel'
3147             }
3148         ],
3149         
3150         zIndex : 10001
3151 });
3152 /*
3153  * - LGPL
3154  *
3155  * messagebox - can be used as a replace
3156  * 
3157  */
3158 /**
3159  * @class Roo.MessageBox
3160  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3161  * Example usage:
3162  *<pre><code>
3163 // Basic alert:
3164 Roo.Msg.alert('Status', 'Changes saved successfully.');
3165
3166 // Prompt for user data:
3167 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3168     if (btn == 'ok'){
3169         // process text value...
3170     }
3171 });
3172
3173 // Show a dialog using config options:
3174 Roo.Msg.show({
3175    title:'Save Changes?',
3176    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3177    buttons: Roo.Msg.YESNOCANCEL,
3178    fn: processResult,
3179    animEl: 'elId'
3180 });
3181 </code></pre>
3182  * @singleton
3183  */
3184 Roo.bootstrap.MessageBox = function(){
3185     var dlg, opt, mask, waitTimer;
3186     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3187     var buttons, activeTextEl, bwidth;
3188
3189     
3190     // private
3191     var handleButton = function(button){
3192         dlg.hide();
3193         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3194     };
3195
3196     // private
3197     var handleHide = function(){
3198         if(opt && opt.cls){
3199             dlg.el.removeClass(opt.cls);
3200         }
3201         //if(waitTimer){
3202         //    Roo.TaskMgr.stop(waitTimer);
3203         //    waitTimer = null;
3204         //}
3205     };
3206
3207     // private
3208     var updateButtons = function(b){
3209         var width = 0;
3210         if(!b){
3211             buttons["ok"].hide();
3212             buttons["cancel"].hide();
3213             buttons["yes"].hide();
3214             buttons["no"].hide();
3215             //dlg.footer.dom.style.display = 'none';
3216             return width;
3217         }
3218         dlg.footerEl.dom.style.display = '';
3219         for(var k in buttons){
3220             if(typeof buttons[k] != "function"){
3221                 if(b[k]){
3222                     buttons[k].show();
3223                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3224                     width += buttons[k].el.getWidth()+15;
3225                 }else{
3226                     buttons[k].hide();
3227                 }
3228             }
3229         }
3230         return width;
3231     };
3232
3233     // private
3234     var handleEsc = function(d, k, e){
3235         if(opt && opt.closable !== false){
3236             dlg.hide();
3237         }
3238         if(e){
3239             e.stopEvent();
3240         }
3241     };
3242
3243     return {
3244         /**
3245          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3246          * @return {Roo.BasicDialog} The BasicDialog element
3247          */
3248         getDialog : function(){
3249            if(!dlg){
3250                 dlg = new Roo.bootstrap.Modal( {
3251                     //draggable: true,
3252                     //resizable:false,
3253                     //constraintoviewport:false,
3254                     //fixedcenter:true,
3255                     //collapsible : false,
3256                     //shim:true,
3257                     //modal: true,
3258                 //    width: 'auto',
3259                   //  height:100,
3260                     //buttonAlign:"center",
3261                     closeClick : function(){
3262                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3263                             handleButton("no");
3264                         }else{
3265                             handleButton("cancel");
3266                         }
3267                     }
3268                 });
3269                 dlg.render();
3270                 dlg.on("hide", handleHide);
3271                 mask = dlg.mask;
3272                 //dlg.addKeyListener(27, handleEsc);
3273                 buttons = {};
3274                 this.buttons = buttons;
3275                 var bt = this.buttonText;
3276                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3277                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3278                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3279                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3280                 //Roo.log(buttons);
3281                 bodyEl = dlg.bodyEl.createChild({
3282
3283                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3284                         '<textarea class="roo-mb-textarea"></textarea>' +
3285                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3286                 });
3287                 msgEl = bodyEl.dom.firstChild;
3288                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3289                 textboxEl.enableDisplayMode();
3290                 textboxEl.addKeyListener([10,13], function(){
3291                     if(dlg.isVisible() && opt && opt.buttons){
3292                         if(opt.buttons.ok){
3293                             handleButton("ok");
3294                         }else if(opt.buttons.yes){
3295                             handleButton("yes");
3296                         }
3297                     }
3298                 });
3299                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3300                 textareaEl.enableDisplayMode();
3301                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3302                 progressEl.enableDisplayMode();
3303                 
3304                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3305                 var pf = progressEl.dom.firstChild;
3306                 if (pf) {
3307                     pp = Roo.get(pf.firstChild);
3308                     pp.setHeight(pf.offsetHeight);
3309                 }
3310                 
3311             }
3312             return dlg;
3313         },
3314
3315         /**
3316          * Updates the message box body text
3317          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3318          * the XHTML-compliant non-breaking space character '&amp;#160;')
3319          * @return {Roo.MessageBox} This message box
3320          */
3321         updateText : function(text)
3322         {
3323             if(!dlg.isVisible() && !opt.width){
3324                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3325                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3326             }
3327             msgEl.innerHTML = text || '&#160;';
3328       
3329             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3330             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3331             var w = Math.max(
3332                     Math.min(opt.width || cw , this.maxWidth), 
3333                     Math.max(opt.minWidth || this.minWidth, bwidth)
3334             );
3335             if(opt.prompt){
3336                 activeTextEl.setWidth(w);
3337             }
3338             if(dlg.isVisible()){
3339                 dlg.fixedcenter = false;
3340             }
3341             // to big, make it scroll. = But as usual stupid IE does not support
3342             // !important..
3343             
3344             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3345                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3346                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3347             } else {
3348                 bodyEl.dom.style.height = '';
3349                 bodyEl.dom.style.overflowY = '';
3350             }
3351             if (cw > w) {
3352                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3353             } else {
3354                 bodyEl.dom.style.overflowX = '';
3355             }
3356             
3357             dlg.setContentSize(w, bodyEl.getHeight());
3358             if(dlg.isVisible()){
3359                 dlg.fixedcenter = true;
3360             }
3361             return this;
3362         },
3363
3364         /**
3365          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3366          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3367          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3368          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3369          * @return {Roo.MessageBox} This message box
3370          */
3371         updateProgress : function(value, text){
3372             if(text){
3373                 this.updateText(text);
3374             }
3375             
3376             if (pp) { // weird bug on my firefox - for some reason this is not defined
3377                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3378                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3379             }
3380             return this;
3381         },        
3382
3383         /**
3384          * Returns true if the message box is currently displayed
3385          * @return {Boolean} True if the message box is visible, else false
3386          */
3387         isVisible : function(){
3388             return dlg && dlg.isVisible();  
3389         },
3390
3391         /**
3392          * Hides the message box if it is displayed
3393          */
3394         hide : function(){
3395             if(this.isVisible()){
3396                 dlg.hide();
3397             }  
3398         },
3399
3400         /**
3401          * Displays a new message box, or reinitializes an existing message box, based on the config options
3402          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3403          * The following config object properties are supported:
3404          * <pre>
3405 Property    Type             Description
3406 ----------  ---------------  ------------------------------------------------------------------------------------
3407 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3408                                    closes (defaults to undefined)
3409 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3410                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3411 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3412                                    progress and wait dialogs will ignore this property and always hide the
3413                                    close button as they can only be closed programmatically.
3414 cls               String           A custom CSS class to apply to the message box element
3415 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3416                                    displayed (defaults to 75)
3417 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3418                                    function will be btn (the name of the button that was clicked, if applicable,
3419                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3420                                    Progress and wait dialogs will ignore this option since they do not respond to
3421                                    user actions and can only be closed programmatically, so any required function
3422                                    should be called by the same code after it closes the dialog.
3423 icon              String           A CSS class that provides a background image to be used as an icon for
3424                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3425 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3426 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3427 modal             Boolean          False to allow user interaction with the page while the message box is
3428                                    displayed (defaults to true)
3429 msg               String           A string that will replace the existing message box body text (defaults
3430                                    to the XHTML-compliant non-breaking space character '&#160;')
3431 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3432 progress          Boolean          True to display a progress bar (defaults to false)
3433 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3434 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3435 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3436 title             String           The title text
3437 value             String           The string value to set into the active textbox element if displayed
3438 wait              Boolean          True to display a progress bar (defaults to false)
3439 width             Number           The width of the dialog in pixels
3440 </pre>
3441          *
3442          * Example usage:
3443          * <pre><code>
3444 Roo.Msg.show({
3445    title: 'Address',
3446    msg: 'Please enter your address:',
3447    width: 300,
3448    buttons: Roo.MessageBox.OKCANCEL,
3449    multiline: true,
3450    fn: saveAddress,
3451    animEl: 'addAddressBtn'
3452 });
3453 </code></pre>
3454          * @param {Object} config Configuration options
3455          * @return {Roo.MessageBox} This message box
3456          */
3457         show : function(options)
3458         {
3459             
3460             // this causes nightmares if you show one dialog after another
3461             // especially on callbacks..
3462              
3463             if(this.isVisible()){
3464                 
3465                 this.hide();
3466                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3467                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3468                 Roo.log("New Dialog Message:" +  options.msg )
3469                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3470                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3471                 
3472             }
3473             var d = this.getDialog();
3474             opt = options;
3475             d.setTitle(opt.title || "&#160;");
3476             d.closeEl.setDisplayed(opt.closable !== false);
3477             activeTextEl = textboxEl;
3478             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3479             if(opt.prompt){
3480                 if(opt.multiline){
3481                     textboxEl.hide();
3482                     textareaEl.show();
3483                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3484                         opt.multiline : this.defaultTextHeight);
3485                     activeTextEl = textareaEl;
3486                 }else{
3487                     textboxEl.show();
3488                     textareaEl.hide();
3489                 }
3490             }else{
3491                 textboxEl.hide();
3492                 textareaEl.hide();
3493             }
3494             progressEl.setDisplayed(opt.progress === true);
3495             this.updateProgress(0);
3496             activeTextEl.dom.value = opt.value || "";
3497             if(opt.prompt){
3498                 dlg.setDefaultButton(activeTextEl);
3499             }else{
3500                 var bs = opt.buttons;
3501                 var db = null;
3502                 if(bs && bs.ok){
3503                     db = buttons["ok"];
3504                 }else if(bs && bs.yes){
3505                     db = buttons["yes"];
3506                 }
3507                 dlg.setDefaultButton(db);
3508             }
3509             bwidth = updateButtons(opt.buttons);
3510             this.updateText(opt.msg);
3511             if(opt.cls){
3512                 d.el.addClass(opt.cls);
3513             }
3514             d.proxyDrag = opt.proxyDrag === true;
3515             d.modal = opt.modal !== false;
3516             d.mask = opt.modal !== false ? mask : false;
3517             if(!d.isVisible()){
3518                 // force it to the end of the z-index stack so it gets a cursor in FF
3519                 document.body.appendChild(dlg.el.dom);
3520                 d.animateTarget = null;
3521                 d.show(options.animEl);
3522             }
3523             return this;
3524         },
3525
3526         /**
3527          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3528          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3529          * and closing the message box when the process is complete.
3530          * @param {String} title The title bar text
3531          * @param {String} msg The message box body text
3532          * @return {Roo.MessageBox} This message box
3533          */
3534         progress : function(title, msg){
3535             this.show({
3536                 title : title,
3537                 msg : msg,
3538                 buttons: false,
3539                 progress:true,
3540                 closable:false,
3541                 minWidth: this.minProgressWidth,
3542                 modal : true
3543             });
3544             return this;
3545         },
3546
3547         /**
3548          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3549          * If a callback function is passed it will be called after the user clicks the button, and the
3550          * id of the button that was clicked will be passed as the only parameter to the callback
3551          * (could also be the top-right close button).
3552          * @param {String} title The title bar text
3553          * @param {String} msg The message box body text
3554          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3555          * @param {Object} scope (optional) The scope of the callback function
3556          * @return {Roo.MessageBox} This message box
3557          */
3558         alert : function(title, msg, fn, scope)
3559         {
3560             this.show({
3561                 title : title,
3562                 msg : msg,
3563                 buttons: this.OK,
3564                 fn: fn,
3565                 closable : false,
3566                 scope : scope,
3567                 modal : true
3568             });
3569             return this;
3570         },
3571
3572         /**
3573          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3574          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3575          * You are responsible for closing the message box when the process is complete.
3576          * @param {String} msg The message box body text
3577          * @param {String} title (optional) The title bar text
3578          * @return {Roo.MessageBox} This message box
3579          */
3580         wait : function(msg, title){
3581             this.show({
3582                 title : title,
3583                 msg : msg,
3584                 buttons: false,
3585                 closable:false,
3586                 progress:true,
3587                 modal:true,
3588                 width:300,
3589                 wait:true
3590             });
3591             waitTimer = Roo.TaskMgr.start({
3592                 run: function(i){
3593                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3594                 },
3595                 interval: 1000
3596             });
3597             return this;
3598         },
3599
3600         /**
3601          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3602          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3603          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3604          * @param {String} title The title bar text
3605          * @param {String} msg The message box body text
3606          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3607          * @param {Object} scope (optional) The scope of the callback function
3608          * @return {Roo.MessageBox} This message box
3609          */
3610         confirm : function(title, msg, fn, scope){
3611             this.show({
3612                 title : title,
3613                 msg : msg,
3614                 buttons: this.YESNO,
3615                 fn: fn,
3616                 scope : scope,
3617                 modal : true
3618             });
3619             return this;
3620         },
3621
3622         /**
3623          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3624          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3625          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3626          * (could also be the top-right close button) and the text that was entered will be passed as the two
3627          * parameters to the callback.
3628          * @param {String} title The title bar text
3629          * @param {String} msg The message box body text
3630          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3631          * @param {Object} scope (optional) The scope of the callback function
3632          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3633          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3634          * @return {Roo.MessageBox} This message box
3635          */
3636         prompt : function(title, msg, fn, scope, multiline){
3637             this.show({
3638                 title : title,
3639                 msg : msg,
3640                 buttons: this.OKCANCEL,
3641                 fn: fn,
3642                 minWidth:250,
3643                 scope : scope,
3644                 prompt:true,
3645                 multiline: multiline,
3646                 modal : true
3647             });
3648             return this;
3649         },
3650
3651         /**
3652          * Button config that displays a single OK button
3653          * @type Object
3654          */
3655         OK : {ok:true},
3656         /**
3657          * Button config that displays Yes and No buttons
3658          * @type Object
3659          */
3660         YESNO : {yes:true, no:true},
3661         /**
3662          * Button config that displays OK and Cancel buttons
3663          * @type Object
3664          */
3665         OKCANCEL : {ok:true, cancel:true},
3666         /**
3667          * Button config that displays Yes, No and Cancel buttons
3668          * @type Object
3669          */
3670         YESNOCANCEL : {yes:true, no:true, cancel:true},
3671
3672         /**
3673          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3674          * @type Number
3675          */
3676         defaultTextHeight : 75,
3677         /**
3678          * The maximum width in pixels of the message box (defaults to 600)
3679          * @type Number
3680          */
3681         maxWidth : 600,
3682         /**
3683          * The minimum width in pixels of the message box (defaults to 100)
3684          * @type Number
3685          */
3686         minWidth : 100,
3687         /**
3688          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3689          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3690          * @type Number
3691          */
3692         minProgressWidth : 250,
3693         /**
3694          * An object containing the default button text strings that can be overriden for localized language support.
3695          * Supported properties are: ok, cancel, yes and no.
3696          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3697          * @type Object
3698          */
3699         buttonText : {
3700             ok : "OK",
3701             cancel : "Cancel",
3702             yes : "Yes",
3703             no : "No"
3704         }
3705     };
3706 }();
3707
3708 /**
3709  * Shorthand for {@link Roo.MessageBox}
3710  */
3711 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3712 Roo.Msg = Roo.Msg || Roo.MessageBox;
3713 /*
3714  * - LGPL
3715  *
3716  * navbar
3717  * 
3718  */
3719
3720 /**
3721  * @class Roo.bootstrap.Navbar
3722  * @extends Roo.bootstrap.Component
3723  * Bootstrap Navbar class
3724
3725  * @constructor
3726  * Create a new Navbar
3727  * @param {Object} config The config object
3728  */
3729
3730
3731 Roo.bootstrap.Navbar = function(config){
3732     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3733     this.addEvents({
3734         // raw events
3735         /**
3736          * @event beforetoggle
3737          * Fire before toggle the menu
3738          * @param {Roo.EventObject} e
3739          */
3740         "beforetoggle" : true
3741     });
3742 };
3743
3744 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3745     
3746     
3747    
3748     // private
3749     navItems : false,
3750     loadMask : false,
3751     
3752     
3753     getAutoCreate : function(){
3754         
3755         
3756         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3757         
3758     },
3759     
3760     initEvents :function ()
3761     {
3762         //Roo.log(this.el.select('.navbar-toggle',true));
3763         this.el.select('.navbar-toggle',true).on('click', function() {
3764             if(this.fireEvent('beforetoggle', this) !== false){
3765                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3766             }
3767             
3768         }, this);
3769         
3770         var mark = {
3771             tag: "div",
3772             cls:"x-dlg-mask"
3773         };
3774         
3775         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3776         
3777         var size = this.el.getSize();
3778         this.maskEl.setSize(size.width, size.height);
3779         this.maskEl.enableDisplayMode("block");
3780         this.maskEl.hide();
3781         
3782         if(this.loadMask){
3783             this.maskEl.show();
3784         }
3785     },
3786     
3787     
3788     getChildContainer : function()
3789     {
3790         if (this.el.select('.collapse').getCount()) {
3791             return this.el.select('.collapse',true).first();
3792         }
3793         
3794         return this.el;
3795     },
3796     
3797     mask : function()
3798     {
3799         this.maskEl.show();
3800     },
3801     
3802     unmask : function()
3803     {
3804         this.maskEl.hide();
3805     } 
3806     
3807     
3808     
3809     
3810 });
3811
3812
3813
3814  
3815
3816  /*
3817  * - LGPL
3818  *
3819  * navbar
3820  * 
3821  */
3822
3823 /**
3824  * @class Roo.bootstrap.NavSimplebar
3825  * @extends Roo.bootstrap.Navbar
3826  * Bootstrap Sidebar class
3827  *
3828  * @cfg {Boolean} inverse is inverted color
3829  * 
3830  * @cfg {String} type (nav | pills | tabs)
3831  * @cfg {Boolean} arrangement stacked | justified
3832  * @cfg {String} align (left | right) alignment
3833  * 
3834  * @cfg {Boolean} main (true|false) main nav bar? default false
3835  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3836  * 
3837  * @cfg {String} tag (header|footer|nav|div) default is nav 
3838
3839  * 
3840  * 
3841  * 
3842  * @constructor
3843  * Create a new Sidebar
3844  * @param {Object} config The config object
3845  */
3846
3847
3848 Roo.bootstrap.NavSimplebar = function(config){
3849     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3850 };
3851
3852 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3853     
3854     inverse: false,
3855     
3856     type: false,
3857     arrangement: '',
3858     align : false,
3859     
3860     
3861     
3862     main : false,
3863     
3864     
3865     tag : false,
3866     
3867     
3868     getAutoCreate : function(){
3869         
3870         
3871         var cfg = {
3872             tag : this.tag || 'div',
3873             cls : 'navbar'
3874         };
3875           
3876         
3877         cfg.cn = [
3878             {
3879                 cls: 'nav',
3880                 tag : 'ul'
3881             }
3882         ];
3883         
3884          
3885         this.type = this.type || 'nav';
3886         if (['tabs','pills'].indexOf(this.type)!==-1) {
3887             cfg.cn[0].cls += ' nav-' + this.type
3888         
3889         
3890         } else {
3891             if (this.type!=='nav') {
3892                 Roo.log('nav type must be nav/tabs/pills')
3893             }
3894             cfg.cn[0].cls += ' navbar-nav'
3895         }
3896         
3897         
3898         
3899         
3900         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3901             cfg.cn[0].cls += ' nav-' + this.arrangement;
3902         }
3903         
3904         
3905         if (this.align === 'right') {
3906             cfg.cn[0].cls += ' navbar-right';
3907         }
3908         
3909         if (this.inverse) {
3910             cfg.cls += ' navbar-inverse';
3911             
3912         }
3913         
3914         
3915         return cfg;
3916     
3917         
3918     }
3919     
3920     
3921     
3922 });
3923
3924
3925
3926  
3927
3928  
3929        /*
3930  * - LGPL
3931  *
3932  * navbar
3933  * 
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         for(var i = 0; i < rows.length; i++) {
7116             
7117             for(var j = 0; w.length; j++) {
7118                 
7119                 var size_cls = w[j].split("-");
7120                 
7121                 if(!Number.isInteger(size_cls[1] * 1)) {
7122                     continue;
7123                 }
7124                 
7125                 if(!this.colModel.config[col_index][size_cls[0]]) {
7126                     continue;
7127                 }
7128                 
7129                 rows[i].classList.replace(
7130                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7131                     "col-"+size_cls[0]+"-"+size_cls[1]
7132                 );
7133                 
7134                 this.colModel.config[col_index][size_cls[0]] = size_cls[1]
7135             }
7136         }
7137     }
7138 });
7139
7140  
7141
7142  /*
7143  * - LGPL
7144  *
7145  * table cell
7146  * 
7147  */
7148
7149 /**
7150  * @class Roo.bootstrap.TableCell
7151  * @extends Roo.bootstrap.Component
7152  * Bootstrap TableCell class
7153  * @cfg {String} html cell contain text
7154  * @cfg {String} cls cell class
7155  * @cfg {String} tag cell tag (td|th) default td
7156  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7157  * @cfg {String} align Aligns the content in a cell
7158  * @cfg {String} axis Categorizes cells
7159  * @cfg {String} bgcolor Specifies the background color of a cell
7160  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7161  * @cfg {Number} colspan Specifies the number of columns a cell should span
7162  * @cfg {String} headers Specifies one or more header cells a cell is related to
7163  * @cfg {Number} height Sets the height of a cell
7164  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7165  * @cfg {Number} rowspan Sets the number of rows a cell should span
7166  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7167  * @cfg {String} valign Vertical aligns the content in a cell
7168  * @cfg {Number} width Specifies the width of a cell
7169  * 
7170  * @constructor
7171  * Create a new TableCell
7172  * @param {Object} config The config object
7173  */
7174
7175 Roo.bootstrap.TableCell = function(config){
7176     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7177 };
7178
7179 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7180     
7181     html: false,
7182     cls: false,
7183     tag: false,
7184     abbr: false,
7185     align: false,
7186     axis: false,
7187     bgcolor: false,
7188     charoff: false,
7189     colspan: false,
7190     headers: false,
7191     height: false,
7192     nowrap: false,
7193     rowspan: false,
7194     scope: false,
7195     valign: false,
7196     width: false,
7197     
7198     
7199     getAutoCreate : function(){
7200         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7201         
7202         cfg = {
7203             tag: 'td'
7204         };
7205         
7206         if(this.tag){
7207             cfg.tag = this.tag;
7208         }
7209         
7210         if (this.html) {
7211             cfg.html=this.html
7212         }
7213         if (this.cls) {
7214             cfg.cls=this.cls
7215         }
7216         if (this.abbr) {
7217             cfg.abbr=this.abbr
7218         }
7219         if (this.align) {
7220             cfg.align=this.align
7221         }
7222         if (this.axis) {
7223             cfg.axis=this.axis
7224         }
7225         if (this.bgcolor) {
7226             cfg.bgcolor=this.bgcolor
7227         }
7228         if (this.charoff) {
7229             cfg.charoff=this.charoff
7230         }
7231         if (this.colspan) {
7232             cfg.colspan=this.colspan
7233         }
7234         if (this.headers) {
7235             cfg.headers=this.headers
7236         }
7237         if (this.height) {
7238             cfg.height=this.height
7239         }
7240         if (this.nowrap) {
7241             cfg.nowrap=this.nowrap
7242         }
7243         if (this.rowspan) {
7244             cfg.rowspan=this.rowspan
7245         }
7246         if (this.scope) {
7247             cfg.scope=this.scope
7248         }
7249         if (this.valign) {
7250             cfg.valign=this.valign
7251         }
7252         if (this.width) {
7253             cfg.width=this.width
7254         }
7255         
7256         
7257         return cfg;
7258     }
7259    
7260 });
7261
7262  
7263
7264  /*
7265  * - LGPL
7266  *
7267  * table row
7268  * 
7269  */
7270
7271 /**
7272  * @class Roo.bootstrap.TableRow
7273  * @extends Roo.bootstrap.Component
7274  * Bootstrap TableRow class
7275  * @cfg {String} cls row class
7276  * @cfg {String} align Aligns the content in a table row
7277  * @cfg {String} bgcolor Specifies a background color for a table row
7278  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7279  * @cfg {String} valign Vertical aligns the content in a table row
7280  * 
7281  * @constructor
7282  * Create a new TableRow
7283  * @param {Object} config The config object
7284  */
7285
7286 Roo.bootstrap.TableRow = function(config){
7287     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7288 };
7289
7290 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7291     
7292     cls: false,
7293     align: false,
7294     bgcolor: false,
7295     charoff: false,
7296     valign: false,
7297     
7298     getAutoCreate : function(){
7299         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7300         
7301         cfg = {
7302             tag: 'tr'
7303         };
7304             
7305         if(this.cls){
7306             cfg.cls = this.cls;
7307         }
7308         if(this.align){
7309             cfg.align = this.align;
7310         }
7311         if(this.bgcolor){
7312             cfg.bgcolor = this.bgcolor;
7313         }
7314         if(this.charoff){
7315             cfg.charoff = this.charoff;
7316         }
7317         if(this.valign){
7318             cfg.valign = this.valign;
7319         }
7320         
7321         return cfg;
7322     }
7323    
7324 });
7325
7326  
7327
7328  /*
7329  * - LGPL
7330  *
7331  * table body
7332  * 
7333  */
7334
7335 /**
7336  * @class Roo.bootstrap.TableBody
7337  * @extends Roo.bootstrap.Component
7338  * Bootstrap TableBody class
7339  * @cfg {String} cls element class
7340  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7341  * @cfg {String} align Aligns the content inside the element
7342  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7343  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7344  * 
7345  * @constructor
7346  * Create a new TableBody
7347  * @param {Object} config The config object
7348  */
7349
7350 Roo.bootstrap.TableBody = function(config){
7351     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7352 };
7353
7354 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7355     
7356     cls: false,
7357     tag: false,
7358     align: false,
7359     charoff: false,
7360     valign: false,
7361     
7362     getAutoCreate : function(){
7363         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7364         
7365         cfg = {
7366             tag: 'tbody'
7367         };
7368             
7369         if (this.cls) {
7370             cfg.cls=this.cls
7371         }
7372         if(this.tag){
7373             cfg.tag = this.tag;
7374         }
7375         
7376         if(this.align){
7377             cfg.align = this.align;
7378         }
7379         if(this.charoff){
7380             cfg.charoff = this.charoff;
7381         }
7382         if(this.valign){
7383             cfg.valign = this.valign;
7384         }
7385         
7386         return cfg;
7387     }
7388     
7389     
7390 //    initEvents : function()
7391 //    {
7392 //        
7393 //        if(!this.store){
7394 //            return;
7395 //        }
7396 //        
7397 //        this.store = Roo.factory(this.store, Roo.data);
7398 //        this.store.on('load', this.onLoad, this);
7399 //        
7400 //        this.store.load();
7401 //        
7402 //    },
7403 //    
7404 //    onLoad: function () 
7405 //    {   
7406 //        this.fireEvent('load', this);
7407 //    }
7408 //    
7409 //   
7410 });
7411
7412  
7413
7414  /*
7415  * Based on:
7416  * Ext JS Library 1.1.1
7417  * Copyright(c) 2006-2007, Ext JS, LLC.
7418  *
7419  * Originally Released Under LGPL - original licence link has changed is not relivant.
7420  *
7421  * Fork - LGPL
7422  * <script type="text/javascript">
7423  */
7424
7425 // as we use this in bootstrap.
7426 Roo.namespace('Roo.form');
7427  /**
7428  * @class Roo.form.Action
7429  * Internal Class used to handle form actions
7430  * @constructor
7431  * @param {Roo.form.BasicForm} el The form element or its id
7432  * @param {Object} config Configuration options
7433  */
7434
7435  
7436  
7437 // define the action interface
7438 Roo.form.Action = function(form, options){
7439     this.form = form;
7440     this.options = options || {};
7441 };
7442 /**
7443  * Client Validation Failed
7444  * @const 
7445  */
7446 Roo.form.Action.CLIENT_INVALID = 'client';
7447 /**
7448  * Server Validation Failed
7449  * @const 
7450  */
7451 Roo.form.Action.SERVER_INVALID = 'server';
7452  /**
7453  * Connect to Server Failed
7454  * @const 
7455  */
7456 Roo.form.Action.CONNECT_FAILURE = 'connect';
7457 /**
7458  * Reading Data from Server Failed
7459  * @const 
7460  */
7461 Roo.form.Action.LOAD_FAILURE = 'load';
7462
7463 Roo.form.Action.prototype = {
7464     type : 'default',
7465     failureType : undefined,
7466     response : undefined,
7467     result : undefined,
7468
7469     // interface method
7470     run : function(options){
7471
7472     },
7473
7474     // interface method
7475     success : function(response){
7476
7477     },
7478
7479     // interface method
7480     handleResponse : function(response){
7481
7482     },
7483
7484     // default connection failure
7485     failure : function(response){
7486         
7487         this.response = response;
7488         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7489         this.form.afterAction(this, false);
7490     },
7491
7492     processResponse : function(response){
7493         this.response = response;
7494         if(!response.responseText){
7495             return true;
7496         }
7497         this.result = this.handleResponse(response);
7498         return this.result;
7499     },
7500
7501     // utility functions used internally
7502     getUrl : function(appendParams){
7503         var url = this.options.url || this.form.url || this.form.el.dom.action;
7504         if(appendParams){
7505             var p = this.getParams();
7506             if(p){
7507                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7508             }
7509         }
7510         return url;
7511     },
7512
7513     getMethod : function(){
7514         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7515     },
7516
7517     getParams : function(){
7518         var bp = this.form.baseParams;
7519         var p = this.options.params;
7520         if(p){
7521             if(typeof p == "object"){
7522                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7523             }else if(typeof p == 'string' && bp){
7524                 p += '&' + Roo.urlEncode(bp);
7525             }
7526         }else if(bp){
7527             p = Roo.urlEncode(bp);
7528         }
7529         return p;
7530     },
7531
7532     createCallback : function(){
7533         return {
7534             success: this.success,
7535             failure: this.failure,
7536             scope: this,
7537             timeout: (this.form.timeout*1000),
7538             upload: this.form.fileUpload ? this.success : undefined
7539         };
7540     }
7541 };
7542
7543 Roo.form.Action.Submit = function(form, options){
7544     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7545 };
7546
7547 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7548     type : 'submit',
7549
7550     haveProgress : false,
7551     uploadComplete : false,
7552     
7553     // uploadProgress indicator.
7554     uploadProgress : function()
7555     {
7556         if (!this.form.progressUrl) {
7557             return;
7558         }
7559         
7560         if (!this.haveProgress) {
7561             Roo.MessageBox.progress("Uploading", "Uploading");
7562         }
7563         if (this.uploadComplete) {
7564            Roo.MessageBox.hide();
7565            return;
7566         }
7567         
7568         this.haveProgress = true;
7569    
7570         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7571         
7572         var c = new Roo.data.Connection();
7573         c.request({
7574             url : this.form.progressUrl,
7575             params: {
7576                 id : uid
7577             },
7578             method: 'GET',
7579             success : function(req){
7580                //console.log(data);
7581                 var rdata = false;
7582                 var edata;
7583                 try  {
7584                    rdata = Roo.decode(req.responseText)
7585                 } catch (e) {
7586                     Roo.log("Invalid data from server..");
7587                     Roo.log(edata);
7588                     return;
7589                 }
7590                 if (!rdata || !rdata.success) {
7591                     Roo.log(rdata);
7592                     Roo.MessageBox.alert(Roo.encode(rdata));
7593                     return;
7594                 }
7595                 var data = rdata.data;
7596                 
7597                 if (this.uploadComplete) {
7598                    Roo.MessageBox.hide();
7599                    return;
7600                 }
7601                    
7602                 if (data){
7603                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7604                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7605                     );
7606                 }
7607                 this.uploadProgress.defer(2000,this);
7608             },
7609        
7610             failure: function(data) {
7611                 Roo.log('progress url failed ');
7612                 Roo.log(data);
7613             },
7614             scope : this
7615         });
7616            
7617     },
7618     
7619     
7620     run : function()
7621     {
7622         // run get Values on the form, so it syncs any secondary forms.
7623         this.form.getValues();
7624         
7625         var o = this.options;
7626         var method = this.getMethod();
7627         var isPost = method == 'POST';
7628         if(o.clientValidation === false || this.form.isValid()){
7629             
7630             if (this.form.progressUrl) {
7631                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7632                     (new Date() * 1) + '' + Math.random());
7633                     
7634             } 
7635             
7636             
7637             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7638                 form:this.form.el.dom,
7639                 url:this.getUrl(!isPost),
7640                 method: method,
7641                 params:isPost ? this.getParams() : null,
7642                 isUpload: this.form.fileUpload
7643             }));
7644             
7645             this.uploadProgress();
7646
7647         }else if (o.clientValidation !== false){ // client validation failed
7648             this.failureType = Roo.form.Action.CLIENT_INVALID;
7649             this.form.afterAction(this, false);
7650         }
7651     },
7652
7653     success : function(response)
7654     {
7655         this.uploadComplete= true;
7656         if (this.haveProgress) {
7657             Roo.MessageBox.hide();
7658         }
7659         
7660         
7661         var result = this.processResponse(response);
7662         if(result === true || result.success){
7663             this.form.afterAction(this, true);
7664             return;
7665         }
7666         if(result.errors){
7667             this.form.markInvalid(result.errors);
7668             this.failureType = Roo.form.Action.SERVER_INVALID;
7669         }
7670         this.form.afterAction(this, false);
7671     },
7672     failure : function(response)
7673     {
7674         this.uploadComplete= true;
7675         if (this.haveProgress) {
7676             Roo.MessageBox.hide();
7677         }
7678         
7679         this.response = response;
7680         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7681         this.form.afterAction(this, false);
7682     },
7683     
7684     handleResponse : function(response){
7685         if(this.form.errorReader){
7686             var rs = this.form.errorReader.read(response);
7687             var errors = [];
7688             if(rs.records){
7689                 for(var i = 0, len = rs.records.length; i < len; i++) {
7690                     var r = rs.records[i];
7691                     errors[i] = r.data;
7692                 }
7693             }
7694             if(errors.length < 1){
7695                 errors = null;
7696             }
7697             return {
7698                 success : rs.success,
7699                 errors : errors
7700             };
7701         }
7702         var ret = false;
7703         try {
7704             ret = Roo.decode(response.responseText);
7705         } catch (e) {
7706             ret = {
7707                 success: false,
7708                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7709                 errors : []
7710             };
7711         }
7712         return ret;
7713         
7714     }
7715 });
7716
7717
7718 Roo.form.Action.Load = function(form, options){
7719     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7720     this.reader = this.form.reader;
7721 };
7722
7723 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7724     type : 'load',
7725
7726     run : function(){
7727         
7728         Roo.Ajax.request(Roo.apply(
7729                 this.createCallback(), {
7730                     method:this.getMethod(),
7731                     url:this.getUrl(false),
7732                     params:this.getParams()
7733         }));
7734     },
7735
7736     success : function(response){
7737         
7738         var result = this.processResponse(response);
7739         if(result === true || !result.success || !result.data){
7740             this.failureType = Roo.form.Action.LOAD_FAILURE;
7741             this.form.afterAction(this, false);
7742             return;
7743         }
7744         this.form.clearInvalid();
7745         this.form.setValues(result.data);
7746         this.form.afterAction(this, true);
7747     },
7748
7749     handleResponse : function(response){
7750         if(this.form.reader){
7751             var rs = this.form.reader.read(response);
7752             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7753             return {
7754                 success : rs.success,
7755                 data : data
7756             };
7757         }
7758         return Roo.decode(response.responseText);
7759     }
7760 });
7761
7762 Roo.form.Action.ACTION_TYPES = {
7763     'load' : Roo.form.Action.Load,
7764     'submit' : Roo.form.Action.Submit
7765 };/*
7766  * - LGPL
7767  *
7768  * form
7769  *
7770  */
7771
7772 /**
7773  * @class Roo.bootstrap.Form
7774  * @extends Roo.bootstrap.Component
7775  * Bootstrap Form class
7776  * @cfg {String} method  GET | POST (default POST)
7777  * @cfg {String} labelAlign top | left (default top)
7778  * @cfg {String} align left  | right - for navbars
7779  * @cfg {Boolean} loadMask load mask when submit (default true)
7780
7781  *
7782  * @constructor
7783  * Create a new Form
7784  * @param {Object} config The config object
7785  */
7786
7787
7788 Roo.bootstrap.Form = function(config){
7789     
7790     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7791     
7792     Roo.bootstrap.Form.popover.apply();
7793     
7794     this.addEvents({
7795         /**
7796          * @event clientvalidation
7797          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7798          * @param {Form} this
7799          * @param {Boolean} valid true if the form has passed client-side validation
7800          */
7801         clientvalidation: true,
7802         /**
7803          * @event beforeaction
7804          * Fires before any action is performed. Return false to cancel the action.
7805          * @param {Form} this
7806          * @param {Action} action The action to be performed
7807          */
7808         beforeaction: true,
7809         /**
7810          * @event actionfailed
7811          * Fires when an action fails.
7812          * @param {Form} this
7813          * @param {Action} action The action that failed
7814          */
7815         actionfailed : true,
7816         /**
7817          * @event actioncomplete
7818          * Fires when an action is completed.
7819          * @param {Form} this
7820          * @param {Action} action The action that completed
7821          */
7822         actioncomplete : true
7823     });
7824 };
7825
7826 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7827
7828      /**
7829      * @cfg {String} method
7830      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7831      */
7832     method : 'POST',
7833     /**
7834      * @cfg {String} url
7835      * The URL to use for form actions if one isn't supplied in the action options.
7836      */
7837     /**
7838      * @cfg {Boolean} fileUpload
7839      * Set to true if this form is a file upload.
7840      */
7841
7842     /**
7843      * @cfg {Object} baseParams
7844      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7845      */
7846
7847     /**
7848      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7849      */
7850     timeout: 30,
7851     /**
7852      * @cfg {Sting} align (left|right) for navbar forms
7853      */
7854     align : 'left',
7855
7856     // private
7857     activeAction : null,
7858
7859     /**
7860      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7861      * element by passing it or its id or mask the form itself by passing in true.
7862      * @type Mixed
7863      */
7864     waitMsgTarget : false,
7865
7866     loadMask : true,
7867     
7868     /**
7869      * @cfg {Boolean} errorMask (true|false) default false
7870      */
7871     errorMask : false,
7872     
7873     /**
7874      * @cfg {Number} maskOffset Default 100
7875      */
7876     maskOffset : 100,
7877     
7878     /**
7879      * @cfg {Boolean} maskBody
7880      */
7881     maskBody : false,
7882
7883     getAutoCreate : function(){
7884
7885         var cfg = {
7886             tag: 'form',
7887             method : this.method || 'POST',
7888             id : this.id || Roo.id(),
7889             cls : ''
7890         };
7891         if (this.parent().xtype.match(/^Nav/)) {
7892             cfg.cls = 'navbar-form navbar-' + this.align;
7893
7894         }
7895
7896         if (this.labelAlign == 'left' ) {
7897             cfg.cls += ' form-horizontal';
7898         }
7899
7900
7901         return cfg;
7902     },
7903     initEvents : function()
7904     {
7905         this.el.on('submit', this.onSubmit, this);
7906         // this was added as random key presses on the form where triggering form submit.
7907         this.el.on('keypress', function(e) {
7908             if (e.getCharCode() != 13) {
7909                 return true;
7910             }
7911             // we might need to allow it for textareas.. and some other items.
7912             // check e.getTarget().
7913
7914             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7915                 return true;
7916             }
7917
7918             Roo.log("keypress blocked");
7919
7920             e.preventDefault();
7921             return false;
7922         });
7923         
7924     },
7925     // private
7926     onSubmit : function(e){
7927         e.stopEvent();
7928     },
7929
7930      /**
7931      * Returns true if client-side validation on the form is successful.
7932      * @return Boolean
7933      */
7934     isValid : function(){
7935         var items = this.getItems();
7936         var valid = true;
7937         var target = false;
7938         
7939         items.each(function(f){
7940             
7941             if(f.validate()){
7942                 return;
7943             }
7944             
7945             Roo.log('invalid field: ' + f.name);
7946             
7947             valid = false;
7948
7949             if(!target && f.el.isVisible(true)){
7950                 target = f;
7951             }
7952            
7953         });
7954         
7955         if(this.errorMask && !valid){
7956             Roo.bootstrap.Form.popover.mask(this, target);
7957         }
7958         
7959         return valid;
7960     },
7961     
7962     /**
7963      * Returns true if any fields in this form have changed since their original load.
7964      * @return Boolean
7965      */
7966     isDirty : function(){
7967         var dirty = false;
7968         var items = this.getItems();
7969         items.each(function(f){
7970            if(f.isDirty()){
7971                dirty = true;
7972                return false;
7973            }
7974            return true;
7975         });
7976         return dirty;
7977     },
7978      /**
7979      * Performs a predefined action (submit or load) or custom actions you define on this form.
7980      * @param {String} actionName The name of the action type
7981      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7982      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7983      * accept other config options):
7984      * <pre>
7985 Property          Type             Description
7986 ----------------  ---------------  ----------------------------------------------------------------------------------
7987 url               String           The url for the action (defaults to the form's url)
7988 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7989 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7990 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7991                                    validate the form on the client (defaults to false)
7992      * </pre>
7993      * @return {BasicForm} this
7994      */
7995     doAction : function(action, options){
7996         if(typeof action == 'string'){
7997             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7998         }
7999         if(this.fireEvent('beforeaction', this, action) !== false){
8000             this.beforeAction(action);
8001             action.run.defer(100, action);
8002         }
8003         return this;
8004     },
8005
8006     // private
8007     beforeAction : function(action){
8008         var o = action.options;
8009         
8010         if(this.loadMask){
8011             
8012             if(this.maskBody){
8013                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8014             } else {
8015                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8016             }
8017         }
8018         // not really supported yet.. ??
8019
8020         //if(this.waitMsgTarget === true){
8021         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8022         //}else if(this.waitMsgTarget){
8023         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8024         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8025         //}else {
8026         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8027        // }
8028
8029     },
8030
8031     // private
8032     afterAction : function(action, success){
8033         this.activeAction = null;
8034         var o = action.options;
8035
8036         if(this.loadMask){
8037             
8038             if(this.maskBody){
8039                 Roo.get(document.body).unmask();
8040             } else {
8041                 this.el.unmask();
8042             }
8043         }
8044         
8045         //if(this.waitMsgTarget === true){
8046 //            this.el.unmask();
8047         //}else if(this.waitMsgTarget){
8048         //    this.waitMsgTarget.unmask();
8049         //}else{
8050         //    Roo.MessageBox.updateProgress(1);
8051         //    Roo.MessageBox.hide();
8052        // }
8053         //
8054         if(success){
8055             if(o.reset){
8056                 this.reset();
8057             }
8058             Roo.callback(o.success, o.scope, [this, action]);
8059             this.fireEvent('actioncomplete', this, action);
8060
8061         }else{
8062
8063             // failure condition..
8064             // we have a scenario where updates need confirming.
8065             // eg. if a locking scenario exists..
8066             // we look for { errors : { needs_confirm : true }} in the response.
8067             if (
8068                 (typeof(action.result) != 'undefined')  &&
8069                 (typeof(action.result.errors) != 'undefined')  &&
8070                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8071            ){
8072                 var _t = this;
8073                 Roo.log("not supported yet");
8074                  /*
8075
8076                 Roo.MessageBox.confirm(
8077                     "Change requires confirmation",
8078                     action.result.errorMsg,
8079                     function(r) {
8080                         if (r != 'yes') {
8081                             return;
8082                         }
8083                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8084                     }
8085
8086                 );
8087                 */
8088
8089
8090                 return;
8091             }
8092
8093             Roo.callback(o.failure, o.scope, [this, action]);
8094             // show an error message if no failed handler is set..
8095             if (!this.hasListener('actionfailed')) {
8096                 Roo.log("need to add dialog support");
8097                 /*
8098                 Roo.MessageBox.alert("Error",
8099                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8100                         action.result.errorMsg :
8101                         "Saving Failed, please check your entries or try again"
8102                 );
8103                 */
8104             }
8105
8106             this.fireEvent('actionfailed', this, action);
8107         }
8108
8109     },
8110     /**
8111      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8112      * @param {String} id The value to search for
8113      * @return Field
8114      */
8115     findField : function(id){
8116         var items = this.getItems();
8117         var field = items.get(id);
8118         if(!field){
8119              items.each(function(f){
8120                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8121                     field = f;
8122                     return false;
8123                 }
8124                 return true;
8125             });
8126         }
8127         return field || null;
8128     },
8129      /**
8130      * Mark fields in this form invalid in bulk.
8131      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8132      * @return {BasicForm} this
8133      */
8134     markInvalid : function(errors){
8135         if(errors instanceof Array){
8136             for(var i = 0, len = errors.length; i < len; i++){
8137                 var fieldError = errors[i];
8138                 var f = this.findField(fieldError.id);
8139                 if(f){
8140                     f.markInvalid(fieldError.msg);
8141                 }
8142             }
8143         }else{
8144             var field, id;
8145             for(id in errors){
8146                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8147                     field.markInvalid(errors[id]);
8148                 }
8149             }
8150         }
8151         //Roo.each(this.childForms || [], function (f) {
8152         //    f.markInvalid(errors);
8153         //});
8154
8155         return this;
8156     },
8157
8158     /**
8159      * Set values for fields in this form in bulk.
8160      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8161      * @return {BasicForm} this
8162      */
8163     setValues : function(values){
8164         if(values instanceof Array){ // array of objects
8165             for(var i = 0, len = values.length; i < len; i++){
8166                 var v = values[i];
8167                 var f = this.findField(v.id);
8168                 if(f){
8169                     f.setValue(v.value);
8170                     if(this.trackResetOnLoad){
8171                         f.originalValue = f.getValue();
8172                     }
8173                 }
8174             }
8175         }else{ // object hash
8176             var field, id;
8177             for(id in values){
8178                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8179
8180                     if (field.setFromData &&
8181                         field.valueField &&
8182                         field.displayField &&
8183                         // combos' with local stores can
8184                         // be queried via setValue()
8185                         // to set their value..
8186                         (field.store && !field.store.isLocal)
8187                         ) {
8188                         // it's a combo
8189                         var sd = { };
8190                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8191                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8192                         field.setFromData(sd);
8193
8194                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8195                         
8196                         field.setFromData(values);
8197                         
8198                     } else {
8199                         field.setValue(values[id]);
8200                     }
8201
8202
8203                     if(this.trackResetOnLoad){
8204                         field.originalValue = field.getValue();
8205                     }
8206                 }
8207             }
8208         }
8209
8210         //Roo.each(this.childForms || [], function (f) {
8211         //    f.setValues(values);
8212         //});
8213
8214         return this;
8215     },
8216
8217     /**
8218      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8219      * they are returned as an array.
8220      * @param {Boolean} asString
8221      * @return {Object}
8222      */
8223     getValues : function(asString){
8224         //if (this.childForms) {
8225             // copy values from the child forms
8226         //    Roo.each(this.childForms, function (f) {
8227         //        this.setValues(f.getValues());
8228         //    }, this);
8229         //}
8230
8231
8232
8233         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8234         if(asString === true){
8235             return fs;
8236         }
8237         return Roo.urlDecode(fs);
8238     },
8239
8240     /**
8241      * Returns the fields in this form as an object with key/value pairs.
8242      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8243      * @return {Object}
8244      */
8245     getFieldValues : function(with_hidden)
8246     {
8247         var items = this.getItems();
8248         var ret = {};
8249         items.each(function(f){
8250             
8251             if (!f.getName()) {
8252                 return;
8253             }
8254             
8255             var v = f.getValue();
8256             
8257             if (f.inputType =='radio') {
8258                 if (typeof(ret[f.getName()]) == 'undefined') {
8259                     ret[f.getName()] = ''; // empty..
8260                 }
8261
8262                 if (!f.el.dom.checked) {
8263                     return;
8264
8265                 }
8266                 v = f.el.dom.value;
8267
8268             }
8269             
8270             if(f.xtype == 'MoneyField'){
8271                 ret[f.currencyName] = f.getCurrency();
8272             }
8273
8274             // not sure if this supported any more..
8275             if ((typeof(v) == 'object') && f.getRawValue) {
8276                 v = f.getRawValue() ; // dates..
8277             }
8278             // combo boxes where name != hiddenName...
8279             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8280                 ret[f.name] = f.getRawValue();
8281             }
8282             ret[f.getName()] = v;
8283         });
8284
8285         return ret;
8286     },
8287
8288     /**
8289      * Clears all invalid messages in this form.
8290      * @return {BasicForm} this
8291      */
8292     clearInvalid : function(){
8293         var items = this.getItems();
8294
8295         items.each(function(f){
8296            f.clearInvalid();
8297         });
8298
8299         return this;
8300     },
8301
8302     /**
8303      * Resets this form.
8304      * @return {BasicForm} this
8305      */
8306     reset : function(){
8307         var items = this.getItems();
8308         items.each(function(f){
8309             f.reset();
8310         });
8311
8312         Roo.each(this.childForms || [], function (f) {
8313             f.reset();
8314         });
8315
8316
8317         return this;
8318     },
8319     
8320     getItems : function()
8321     {
8322         var r=new Roo.util.MixedCollection(false, function(o){
8323             return o.id || (o.id = Roo.id());
8324         });
8325         var iter = function(el) {
8326             if (el.inputEl) {
8327                 r.add(el);
8328             }
8329             if (!el.items) {
8330                 return;
8331             }
8332             Roo.each(el.items,function(e) {
8333                 iter(e);
8334             });
8335         };
8336
8337         iter(this);
8338         return r;
8339     },
8340     
8341     hideFields : function(items)
8342     {
8343         Roo.each(items, function(i){
8344             
8345             var f = this.findField(i);
8346             
8347             if(!f){
8348                 return;
8349             }
8350             
8351             f.hide();
8352             
8353         }, this);
8354     },
8355     
8356     showFields : function(items)
8357     {
8358         Roo.each(items, function(i){
8359             
8360             var f = this.findField(i);
8361             
8362             if(!f){
8363                 return;
8364             }
8365             
8366             f.show();
8367             
8368         }, this);
8369     }
8370
8371 });
8372
8373 Roo.apply(Roo.bootstrap.Form, {
8374     
8375     popover : {
8376         
8377         padding : 5,
8378         
8379         isApplied : false,
8380         
8381         isMasked : false,
8382         
8383         form : false,
8384         
8385         target : false,
8386         
8387         toolTip : false,
8388         
8389         intervalID : false,
8390         
8391         maskEl : false,
8392         
8393         apply : function()
8394         {
8395             if(this.isApplied){
8396                 return;
8397             }
8398             
8399             this.maskEl = {
8400                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8401                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8402                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8403                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8404             };
8405             
8406             this.maskEl.top.enableDisplayMode("block");
8407             this.maskEl.left.enableDisplayMode("block");
8408             this.maskEl.bottom.enableDisplayMode("block");
8409             this.maskEl.right.enableDisplayMode("block");
8410             
8411             this.toolTip = new Roo.bootstrap.Tooltip({
8412                 cls : 'roo-form-error-popover',
8413                 alignment : {
8414                     'left' : ['r-l', [-2,0], 'right'],
8415                     'right' : ['l-r', [2,0], 'left'],
8416                     'bottom' : ['tl-bl', [0,2], 'top'],
8417                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8418                 }
8419             });
8420             
8421             this.toolTip.render(Roo.get(document.body));
8422
8423             this.toolTip.el.enableDisplayMode("block");
8424             
8425             Roo.get(document.body).on('click', function(){
8426                 this.unmask();
8427             }, this);
8428             
8429             Roo.get(document.body).on('touchstart', function(){
8430                 this.unmask();
8431             }, this);
8432             
8433             this.isApplied = true
8434         },
8435         
8436         mask : function(form, target)
8437         {
8438             this.form = form;
8439             
8440             this.target = target;
8441             
8442             if(!this.form.errorMask || !target.el){
8443                 return;
8444             }
8445             
8446             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8447             
8448             Roo.log(scrollable);
8449             
8450             var ot = this.target.el.calcOffsetsTo(scrollable);
8451             
8452             var scrollTo = ot[1] - this.form.maskOffset;
8453             
8454             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8455             
8456             scrollable.scrollTo('top', scrollTo);
8457             
8458             var box = this.target.el.getBox();
8459             Roo.log(box);
8460             var zIndex = Roo.bootstrap.Modal.zIndex++;
8461
8462             
8463             this.maskEl.top.setStyle('position', 'absolute');
8464             this.maskEl.top.setStyle('z-index', zIndex);
8465             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8466             this.maskEl.top.setLeft(0);
8467             this.maskEl.top.setTop(0);
8468             this.maskEl.top.show();
8469             
8470             this.maskEl.left.setStyle('position', 'absolute');
8471             this.maskEl.left.setStyle('z-index', zIndex);
8472             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8473             this.maskEl.left.setLeft(0);
8474             this.maskEl.left.setTop(box.y - this.padding);
8475             this.maskEl.left.show();
8476
8477             this.maskEl.bottom.setStyle('position', 'absolute');
8478             this.maskEl.bottom.setStyle('z-index', zIndex);
8479             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8480             this.maskEl.bottom.setLeft(0);
8481             this.maskEl.bottom.setTop(box.bottom + this.padding);
8482             this.maskEl.bottom.show();
8483
8484             this.maskEl.right.setStyle('position', 'absolute');
8485             this.maskEl.right.setStyle('z-index', zIndex);
8486             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8487             this.maskEl.right.setLeft(box.right + this.padding);
8488             this.maskEl.right.setTop(box.y - this.padding);
8489             this.maskEl.right.show();
8490
8491             this.toolTip.bindEl = this.target.el;
8492
8493             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8494
8495             var tip = this.target.blankText;
8496
8497             if(this.target.getValue() !== '' ) {
8498                 
8499                 if (this.target.invalidText.length) {
8500                     tip = this.target.invalidText;
8501                 } else if (this.target.regexText.length){
8502                     tip = this.target.regexText;
8503                 }
8504             }
8505
8506             this.toolTip.show(tip);
8507
8508             this.intervalID = window.setInterval(function() {
8509                 Roo.bootstrap.Form.popover.unmask();
8510             }, 10000);
8511
8512             window.onwheel = function(){ return false;};
8513             
8514             (function(){ this.isMasked = true; }).defer(500, this);
8515             
8516         },
8517         
8518         unmask : function()
8519         {
8520             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8521                 return;
8522             }
8523             
8524             this.maskEl.top.setStyle('position', 'absolute');
8525             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8526             this.maskEl.top.hide();
8527
8528             this.maskEl.left.setStyle('position', 'absolute');
8529             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8530             this.maskEl.left.hide();
8531
8532             this.maskEl.bottom.setStyle('position', 'absolute');
8533             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8534             this.maskEl.bottom.hide();
8535
8536             this.maskEl.right.setStyle('position', 'absolute');
8537             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8538             this.maskEl.right.hide();
8539             
8540             this.toolTip.hide();
8541             
8542             this.toolTip.el.hide();
8543             
8544             window.onwheel = function(){ return true;};
8545             
8546             if(this.intervalID){
8547                 window.clearInterval(this.intervalID);
8548                 this.intervalID = false;
8549             }
8550             
8551             this.isMasked = false;
8552             
8553         }
8554         
8555     }
8556     
8557 });
8558
8559 /*
8560  * Based on:
8561  * Ext JS Library 1.1.1
8562  * Copyright(c) 2006-2007, Ext JS, LLC.
8563  *
8564  * Originally Released Under LGPL - original licence link has changed is not relivant.
8565  *
8566  * Fork - LGPL
8567  * <script type="text/javascript">
8568  */
8569 /**
8570  * @class Roo.form.VTypes
8571  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8572  * @singleton
8573  */
8574 Roo.form.VTypes = function(){
8575     // closure these in so they are only created once.
8576     var alpha = /^[a-zA-Z_]+$/;
8577     var alphanum = /^[a-zA-Z0-9_]+$/;
8578     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8579     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8580
8581     // All these messages and functions are configurable
8582     return {
8583         /**
8584          * The function used to validate email addresses
8585          * @param {String} value The email address
8586          */
8587         'email' : function(v){
8588             return email.test(v);
8589         },
8590         /**
8591          * The error text to display when the email validation function returns false
8592          * @type String
8593          */
8594         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8595         /**
8596          * The keystroke filter mask to be applied on email input
8597          * @type RegExp
8598          */
8599         'emailMask' : /[a-z0-9_\.\-@]/i,
8600
8601         /**
8602          * The function used to validate URLs
8603          * @param {String} value The URL
8604          */
8605         'url' : function(v){
8606             return url.test(v);
8607         },
8608         /**
8609          * The error text to display when the url validation function returns false
8610          * @type String
8611          */
8612         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8613         
8614         /**
8615          * The function used to validate alpha values
8616          * @param {String} value The value
8617          */
8618         'alpha' : function(v){
8619             return alpha.test(v);
8620         },
8621         /**
8622          * The error text to display when the alpha validation function returns false
8623          * @type String
8624          */
8625         'alphaText' : 'This field should only contain letters and _',
8626         /**
8627          * The keystroke filter mask to be applied on alpha input
8628          * @type RegExp
8629          */
8630         'alphaMask' : /[a-z_]/i,
8631
8632         /**
8633          * The function used to validate alphanumeric values
8634          * @param {String} value The value
8635          */
8636         'alphanum' : function(v){
8637             return alphanum.test(v);
8638         },
8639         /**
8640          * The error text to display when the alphanumeric validation function returns false
8641          * @type String
8642          */
8643         'alphanumText' : 'This field should only contain letters, numbers and _',
8644         /**
8645          * The keystroke filter mask to be applied on alphanumeric input
8646          * @type RegExp
8647          */
8648         'alphanumMask' : /[a-z0-9_]/i
8649     };
8650 }();/*
8651  * - LGPL
8652  *
8653  * Input
8654  * 
8655  */
8656
8657 /**
8658  * @class Roo.bootstrap.Input
8659  * @extends Roo.bootstrap.Component
8660  * Bootstrap Input class
8661  * @cfg {Boolean} disabled is it disabled
8662  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8663  * @cfg {String} name name of the input
8664  * @cfg {string} fieldLabel - the label associated
8665  * @cfg {string} placeholder - placeholder to put in text.
8666  * @cfg {string}  before - input group add on before
8667  * @cfg {string} after - input group add on after
8668  * @cfg {string} size - (lg|sm) or leave empty..
8669  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8670  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8671  * @cfg {Number} md colspan out of 12 for computer-sized screens
8672  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8673  * @cfg {string} value default value of the input
8674  * @cfg {Number} labelWidth set the width of label 
8675  * @cfg {Number} labellg set the width of label (1-12)
8676  * @cfg {Number} labelmd set the width of label (1-12)
8677  * @cfg {Number} labelsm set the width of label (1-12)
8678  * @cfg {Number} labelxs set the width of label (1-12)
8679  * @cfg {String} labelAlign (top|left)
8680  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8681  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8682  * @cfg {String} indicatorpos (left|right) default left
8683  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8684  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8685
8686  * @cfg {String} align (left|center|right) Default left
8687  * @cfg {Boolean} forceFeedback (true|false) Default false
8688  * 
8689  * @constructor
8690  * Create a new Input
8691  * @param {Object} config The config object
8692  */
8693
8694 Roo.bootstrap.Input = function(config){
8695     
8696     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8697     
8698     this.addEvents({
8699         /**
8700          * @event focus
8701          * Fires when this field receives input focus.
8702          * @param {Roo.form.Field} this
8703          */
8704         focus : true,
8705         /**
8706          * @event blur
8707          * Fires when this field loses input focus.
8708          * @param {Roo.form.Field} this
8709          */
8710         blur : true,
8711         /**
8712          * @event specialkey
8713          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8714          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8715          * @param {Roo.form.Field} this
8716          * @param {Roo.EventObject} e The event object
8717          */
8718         specialkey : true,
8719         /**
8720          * @event change
8721          * Fires just before the field blurs if the field value has changed.
8722          * @param {Roo.form.Field} this
8723          * @param {Mixed} newValue The new value
8724          * @param {Mixed} oldValue The original value
8725          */
8726         change : true,
8727         /**
8728          * @event invalid
8729          * Fires after the field has been marked as invalid.
8730          * @param {Roo.form.Field} this
8731          * @param {String} msg The validation message
8732          */
8733         invalid : true,
8734         /**
8735          * @event valid
8736          * Fires after the field has been validated with no errors.
8737          * @param {Roo.form.Field} this
8738          */
8739         valid : true,
8740          /**
8741          * @event keyup
8742          * Fires after the key up
8743          * @param {Roo.form.Field} this
8744          * @param {Roo.EventObject}  e The event Object
8745          */
8746         keyup : true
8747     });
8748 };
8749
8750 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8751      /**
8752      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8753       automatic validation (defaults to "keyup").
8754      */
8755     validationEvent : "keyup",
8756      /**
8757      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8758      */
8759     validateOnBlur : true,
8760     /**
8761      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8762      */
8763     validationDelay : 250,
8764      /**
8765      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8766      */
8767     focusClass : "x-form-focus",  // not needed???
8768     
8769        
8770     /**
8771      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8772      */
8773     invalidClass : "has-warning",
8774     
8775     /**
8776      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8777      */
8778     validClass : "has-success",
8779     
8780     /**
8781      * @cfg {Boolean} hasFeedback (true|false) default true
8782      */
8783     hasFeedback : true,
8784     
8785     /**
8786      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8787      */
8788     invalidFeedbackClass : "glyphicon-warning-sign",
8789     
8790     /**
8791      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8792      */
8793     validFeedbackClass : "glyphicon-ok",
8794     
8795     /**
8796      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8797      */
8798     selectOnFocus : false,
8799     
8800      /**
8801      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8802      */
8803     maskRe : null,
8804        /**
8805      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8806      */
8807     vtype : null,
8808     
8809       /**
8810      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8811      */
8812     disableKeyFilter : false,
8813     
8814        /**
8815      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8816      */
8817     disabled : false,
8818      /**
8819      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8820      */
8821     allowBlank : true,
8822     /**
8823      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8824      */
8825     blankText : "Please complete this mandatory field",
8826     
8827      /**
8828      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8829      */
8830     minLength : 0,
8831     /**
8832      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8833      */
8834     maxLength : Number.MAX_VALUE,
8835     /**
8836      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8837      */
8838     minLengthText : "The minimum length for this field is {0}",
8839     /**
8840      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8841      */
8842     maxLengthText : "The maximum length for this field is {0}",
8843   
8844     
8845     /**
8846      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8847      * If available, this function will be called only after the basic validators all return true, and will be passed the
8848      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8849      */
8850     validator : null,
8851     /**
8852      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8853      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8854      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8855      */
8856     regex : null,
8857     /**
8858      * @cfg {String} regexText -- Depricated - use Invalid Text
8859      */
8860     regexText : "",
8861     
8862     /**
8863      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8864      */
8865     invalidText : "",
8866     
8867     
8868     
8869     autocomplete: false,
8870     
8871     
8872     fieldLabel : '',
8873     inputType : 'text',
8874     
8875     name : false,
8876     placeholder: false,
8877     before : false,
8878     after : false,
8879     size : false,
8880     hasFocus : false,
8881     preventMark: false,
8882     isFormField : true,
8883     value : '',
8884     labelWidth : 2,
8885     labelAlign : false,
8886     readOnly : false,
8887     align : false,
8888     formatedValue : false,
8889     forceFeedback : false,
8890     
8891     indicatorpos : 'left',
8892     
8893     labellg : 0,
8894     labelmd : 0,
8895     labelsm : 0,
8896     labelxs : 0,
8897     
8898     capture : '',
8899     accept : '',
8900     
8901     parentLabelAlign : function()
8902     {
8903         var parent = this;
8904         while (parent.parent()) {
8905             parent = parent.parent();
8906             if (typeof(parent.labelAlign) !='undefined') {
8907                 return parent.labelAlign;
8908             }
8909         }
8910         return 'left';
8911         
8912     },
8913     
8914     getAutoCreate : function()
8915     {
8916         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8917         
8918         var id = Roo.id();
8919         
8920         var cfg = {};
8921         
8922         if(this.inputType != 'hidden'){
8923             cfg.cls = 'form-group' //input-group
8924         }
8925         
8926         var input =  {
8927             tag: 'input',
8928             id : id,
8929             type : this.inputType,
8930             value : this.value,
8931             cls : 'form-control',
8932             placeholder : this.placeholder || '',
8933             autocomplete : this.autocomplete || 'new-password'
8934         };
8935         
8936         if(this.capture.length){
8937             input.capture = this.capture;
8938         }
8939         
8940         if(this.accept.length){
8941             input.accept = this.accept + "/*";
8942         }
8943         
8944         if(this.align){
8945             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8946         }
8947         
8948         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8949             input.maxLength = this.maxLength;
8950         }
8951         
8952         if (this.disabled) {
8953             input.disabled=true;
8954         }
8955         
8956         if (this.readOnly) {
8957             input.readonly=true;
8958         }
8959         
8960         if (this.name) {
8961             input.name = this.name;
8962         }
8963         
8964         if (this.size) {
8965             input.cls += ' input-' + this.size;
8966         }
8967         
8968         var settings=this;
8969         ['xs','sm','md','lg'].map(function(size){
8970             if (settings[size]) {
8971                 cfg.cls += ' col-' + size + '-' + settings[size];
8972             }
8973         });
8974         
8975         var inputblock = input;
8976         
8977         var feedback = {
8978             tag: 'span',
8979             cls: 'glyphicon form-control-feedback'
8980         };
8981             
8982         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8983             
8984             inputblock = {
8985                 cls : 'has-feedback',
8986                 cn :  [
8987                     input,
8988                     feedback
8989                 ] 
8990             };  
8991         }
8992         
8993         if (this.before || this.after) {
8994             
8995             inputblock = {
8996                 cls : 'input-group',
8997                 cn :  [] 
8998             };
8999             
9000             if (this.before && typeof(this.before) == 'string') {
9001                 
9002                 inputblock.cn.push({
9003                     tag :'span',
9004                     cls : 'roo-input-before input-group-addon',
9005                     html : this.before
9006                 });
9007             }
9008             if (this.before && typeof(this.before) == 'object') {
9009                 this.before = Roo.factory(this.before);
9010                 
9011                 inputblock.cn.push({
9012                     tag :'span',
9013                     cls : 'roo-input-before input-group-' +
9014                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9015                 });
9016             }
9017             
9018             inputblock.cn.push(input);
9019             
9020             if (this.after && typeof(this.after) == 'string') {
9021                 inputblock.cn.push({
9022                     tag :'span',
9023                     cls : 'roo-input-after input-group-addon',
9024                     html : this.after
9025                 });
9026             }
9027             if (this.after && typeof(this.after) == 'object') {
9028                 this.after = Roo.factory(this.after);
9029                 
9030                 inputblock.cn.push({
9031                     tag :'span',
9032                     cls : 'roo-input-after input-group-' +
9033                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9034                 });
9035             }
9036             
9037             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9038                 inputblock.cls += ' has-feedback';
9039                 inputblock.cn.push(feedback);
9040             }
9041         };
9042         
9043         if (align ==='left' && this.fieldLabel.length) {
9044             
9045             cfg.cls += ' roo-form-group-label-left';
9046             
9047             cfg.cn = [
9048                 {
9049                     tag : 'i',
9050                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9051                     tooltip : 'This field is required'
9052                 },
9053                 {
9054                     tag: 'label',
9055                     'for' :  id,
9056                     cls : 'control-label',
9057                     html : this.fieldLabel
9058
9059                 },
9060                 {
9061                     cls : "", 
9062                     cn: [
9063                         inputblock
9064                     ]
9065                 }
9066             ];
9067             
9068             var labelCfg = cfg.cn[1];
9069             var contentCfg = cfg.cn[2];
9070             
9071             if(this.indicatorpos == 'right'){
9072                 cfg.cn = [
9073                     {
9074                         tag: 'label',
9075                         'for' :  id,
9076                         cls : 'control-label',
9077                         cn : [
9078                             {
9079                                 tag : 'span',
9080                                 html : this.fieldLabel
9081                             },
9082                             {
9083                                 tag : 'i',
9084                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9085                                 tooltip : 'This field is required'
9086                             }
9087                         ]
9088                     },
9089                     {
9090                         cls : "",
9091                         cn: [
9092                             inputblock
9093                         ]
9094                     }
9095
9096                 ];
9097                 
9098                 labelCfg = cfg.cn[0];
9099                 contentCfg = cfg.cn[1];
9100             
9101             }
9102             
9103             if(this.labelWidth > 12){
9104                 labelCfg.style = "width: " + this.labelWidth + 'px';
9105             }
9106             
9107             if(this.labelWidth < 13 && this.labelmd == 0){
9108                 this.labelmd = this.labelWidth;
9109             }
9110             
9111             if(this.labellg > 0){
9112                 labelCfg.cls += ' col-lg-' + this.labellg;
9113                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9114             }
9115             
9116             if(this.labelmd > 0){
9117                 labelCfg.cls += ' col-md-' + this.labelmd;
9118                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9119             }
9120             
9121             if(this.labelsm > 0){
9122                 labelCfg.cls += ' col-sm-' + this.labelsm;
9123                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9124             }
9125             
9126             if(this.labelxs > 0){
9127                 labelCfg.cls += ' col-xs-' + this.labelxs;
9128                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9129             }
9130             
9131             
9132         } else if ( this.fieldLabel.length) {
9133                 
9134             cfg.cn = [
9135                 {
9136                     tag : 'i',
9137                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9138                     tooltip : 'This field is required'
9139                 },
9140                 {
9141                     tag: 'label',
9142                    //cls : 'input-group-addon',
9143                     html : this.fieldLabel
9144
9145                 },
9146
9147                inputblock
9148
9149            ];
9150            
9151            if(this.indicatorpos == 'right'){
9152                 
9153                 cfg.cn = [
9154                     {
9155                         tag: 'label',
9156                        //cls : 'input-group-addon',
9157                         html : this.fieldLabel
9158
9159                     },
9160                     {
9161                         tag : 'i',
9162                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9163                         tooltip : 'This field is required'
9164                     },
9165
9166                    inputblock
9167
9168                ];
9169
9170             }
9171
9172         } else {
9173             
9174             cfg.cn = [
9175
9176                     inputblock
9177
9178             ];
9179                 
9180                 
9181         };
9182         
9183         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9184            cfg.cls += ' navbar-form';
9185         }
9186         
9187         if (this.parentType === 'NavGroup') {
9188            cfg.cls += ' navbar-form';
9189            cfg.tag = 'li';
9190         }
9191         
9192         return cfg;
9193         
9194     },
9195     /**
9196      * return the real input element.
9197      */
9198     inputEl: function ()
9199     {
9200         return this.el.select('input.form-control',true).first();
9201     },
9202     
9203     tooltipEl : function()
9204     {
9205         return this.inputEl();
9206     },
9207     
9208     indicatorEl : function()
9209     {
9210         var indicator = this.el.select('i.roo-required-indicator',true).first();
9211         
9212         if(!indicator){
9213             return false;
9214         }
9215         
9216         return indicator;
9217         
9218     },
9219     
9220     setDisabled : function(v)
9221     {
9222         var i  = this.inputEl().dom;
9223         if (!v) {
9224             i.removeAttribute('disabled');
9225             return;
9226             
9227         }
9228         i.setAttribute('disabled','true');
9229     },
9230     initEvents : function()
9231     {
9232           
9233         this.inputEl().on("keydown" , this.fireKey,  this);
9234         this.inputEl().on("focus", this.onFocus,  this);
9235         this.inputEl().on("blur", this.onBlur,  this);
9236         
9237         this.inputEl().relayEvent('keyup', this);
9238         
9239         this.indicator = this.indicatorEl();
9240         
9241         if(this.indicator){
9242             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9243         }
9244  
9245         // reference to original value for reset
9246         this.originalValue = this.getValue();
9247         //Roo.form.TextField.superclass.initEvents.call(this);
9248         if(this.validationEvent == 'keyup'){
9249             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9250             this.inputEl().on('keyup', this.filterValidation, this);
9251         }
9252         else if(this.validationEvent !== false){
9253             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9254         }
9255         
9256         if(this.selectOnFocus){
9257             this.on("focus", this.preFocus, this);
9258             
9259         }
9260         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9261             this.inputEl().on("keypress", this.filterKeys, this);
9262         } else {
9263             this.inputEl().relayEvent('keypress', this);
9264         }
9265        /* if(this.grow){
9266             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9267             this.el.on("click", this.autoSize,  this);
9268         }
9269         */
9270         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9271             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9272         }
9273         
9274         if (typeof(this.before) == 'object') {
9275             this.before.render(this.el.select('.roo-input-before',true).first());
9276         }
9277         if (typeof(this.after) == 'object') {
9278             this.after.render(this.el.select('.roo-input-after',true).first());
9279         }
9280         
9281         this.inputEl().on('change', this.onChange, this);
9282         
9283     },
9284     filterValidation : function(e){
9285         if(!e.isNavKeyPress()){
9286             this.validationTask.delay(this.validationDelay);
9287         }
9288     },
9289      /**
9290      * Validates the field value
9291      * @return {Boolean} True if the value is valid, else false
9292      */
9293     validate : function(){
9294         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9295         if(this.disabled || this.validateValue(this.getRawValue())){
9296             this.markValid();
9297             return true;
9298         }
9299         
9300         this.markInvalid();
9301         return false;
9302     },
9303     
9304     
9305     /**
9306      * Validates a value according to the field's validation rules and marks the field as invalid
9307      * if the validation fails
9308      * @param {Mixed} value The value to validate
9309      * @return {Boolean} True if the value is valid, else false
9310      */
9311     validateValue : function(value)
9312     {
9313         if(this.getVisibilityEl().hasClass('hidden')){
9314             return true;
9315         }
9316         
9317         if(value.length < 1)  { // if it's blank
9318             if(this.allowBlank){
9319                 return true;
9320             }
9321             return false;
9322         }
9323         
9324         if(value.length < this.minLength){
9325             return false;
9326         }
9327         if(value.length > this.maxLength){
9328             return false;
9329         }
9330         if(this.vtype){
9331             var vt = Roo.form.VTypes;
9332             if(!vt[this.vtype](value, this)){
9333                 return false;
9334             }
9335         }
9336         if(typeof this.validator == "function"){
9337             var msg = this.validator(value);
9338             if(msg !== true){
9339                 return false;
9340             }
9341             if (typeof(msg) == 'string') {
9342                 this.invalidText = msg;
9343             }
9344         }
9345         
9346         if(this.regex && !this.regex.test(value)){
9347             return false;
9348         }
9349         
9350         return true;
9351     },
9352     
9353      // private
9354     fireKey : function(e){
9355         //Roo.log('field ' + e.getKey());
9356         if(e.isNavKeyPress()){
9357             this.fireEvent("specialkey", this, e);
9358         }
9359     },
9360     focus : function (selectText){
9361         if(this.rendered){
9362             this.inputEl().focus();
9363             if(selectText === true){
9364                 this.inputEl().dom.select();
9365             }
9366         }
9367         return this;
9368     } ,
9369     
9370     onFocus : function(){
9371         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9372            // this.el.addClass(this.focusClass);
9373         }
9374         if(!this.hasFocus){
9375             this.hasFocus = true;
9376             this.startValue = this.getValue();
9377             this.fireEvent("focus", this);
9378         }
9379     },
9380     
9381     beforeBlur : Roo.emptyFn,
9382
9383     
9384     // private
9385     onBlur : function(){
9386         this.beforeBlur();
9387         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9388             //this.el.removeClass(this.focusClass);
9389         }
9390         this.hasFocus = false;
9391         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9392             this.validate();
9393         }
9394         var v = this.getValue();
9395         if(String(v) !== String(this.startValue)){
9396             this.fireEvent('change', this, v, this.startValue);
9397         }
9398         this.fireEvent("blur", this);
9399     },
9400     
9401     onChange : function(e)
9402     {
9403         var v = this.getValue();
9404         if(String(v) !== String(this.startValue)){
9405             this.fireEvent('change', this, v, this.startValue);
9406         }
9407         
9408     },
9409     
9410     /**
9411      * Resets the current field value to the originally loaded value and clears any validation messages
9412      */
9413     reset : function(){
9414         this.setValue(this.originalValue);
9415         this.validate();
9416     },
9417      /**
9418      * Returns the name of the field
9419      * @return {Mixed} name The name field
9420      */
9421     getName: function(){
9422         return this.name;
9423     },
9424      /**
9425      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9426      * @return {Mixed} value The field value
9427      */
9428     getValue : function(){
9429         
9430         var v = this.inputEl().getValue();
9431         
9432         return v;
9433     },
9434     /**
9435      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9436      * @return {Mixed} value The field value
9437      */
9438     getRawValue : function(){
9439         var v = this.inputEl().getValue();
9440         
9441         return v;
9442     },
9443     
9444     /**
9445      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9446      * @param {Mixed} value The value to set
9447      */
9448     setRawValue : function(v){
9449         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9450     },
9451     
9452     selectText : function(start, end){
9453         var v = this.getRawValue();
9454         if(v.length > 0){
9455             start = start === undefined ? 0 : start;
9456             end = end === undefined ? v.length : end;
9457             var d = this.inputEl().dom;
9458             if(d.setSelectionRange){
9459                 d.setSelectionRange(start, end);
9460             }else if(d.createTextRange){
9461                 var range = d.createTextRange();
9462                 range.moveStart("character", start);
9463                 range.moveEnd("character", v.length-end);
9464                 range.select();
9465             }
9466         }
9467     },
9468     
9469     /**
9470      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9471      * @param {Mixed} value The value to set
9472      */
9473     setValue : function(v){
9474         this.value = v;
9475         if(this.rendered){
9476             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9477             this.validate();
9478         }
9479     },
9480     
9481     /*
9482     processValue : function(value){
9483         if(this.stripCharsRe){
9484             var newValue = value.replace(this.stripCharsRe, '');
9485             if(newValue !== value){
9486                 this.setRawValue(newValue);
9487                 return newValue;
9488             }
9489         }
9490         return value;
9491     },
9492   */
9493     preFocus : function(){
9494         
9495         if(this.selectOnFocus){
9496             this.inputEl().dom.select();
9497         }
9498     },
9499     filterKeys : function(e){
9500         var k = e.getKey();
9501         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9502             return;
9503         }
9504         var c = e.getCharCode(), cc = String.fromCharCode(c);
9505         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9506             return;
9507         }
9508         if(!this.maskRe.test(cc)){
9509             e.stopEvent();
9510         }
9511     },
9512      /**
9513      * Clear any invalid styles/messages for this field
9514      */
9515     clearInvalid : function(){
9516         
9517         if(!this.el || this.preventMark){ // not rendered
9518             return;
9519         }
9520         
9521      
9522         this.el.removeClass(this.invalidClass);
9523         
9524         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9525             
9526             var feedback = this.el.select('.form-control-feedback', true).first();
9527             
9528             if(feedback){
9529                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9530             }
9531             
9532         }
9533         
9534         if(this.indicator){
9535             this.indicator.removeClass('visible');
9536             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9537         }
9538         
9539         this.fireEvent('valid', this);
9540     },
9541     
9542      /**
9543      * Mark this field as valid
9544      */
9545     markValid : function()
9546     {
9547         if(!this.el  || this.preventMark){ // not rendered...
9548             return;
9549         }
9550         
9551         this.el.removeClass([this.invalidClass, this.validClass]);
9552         
9553         var feedback = this.el.select('.form-control-feedback', true).first();
9554             
9555         if(feedback){
9556             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9557         }
9558         
9559         if(this.indicator){
9560             this.indicator.removeClass('visible');
9561             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9562         }
9563         
9564         if(this.disabled){
9565             return;
9566         }
9567         
9568         if(this.allowBlank && !this.getRawValue().length){
9569             return;
9570         }
9571         
9572         this.el.addClass(this.validClass);
9573         
9574         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9575             
9576             var feedback = this.el.select('.form-control-feedback', true).first();
9577             
9578             if(feedback){
9579                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9580                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9581             }
9582             
9583         }
9584         
9585         this.fireEvent('valid', this);
9586     },
9587     
9588      /**
9589      * Mark this field as invalid
9590      * @param {String} msg The validation message
9591      */
9592     markInvalid : function(msg)
9593     {
9594         if(!this.el  || this.preventMark){ // not rendered
9595             return;
9596         }
9597         
9598         this.el.removeClass([this.invalidClass, this.validClass]);
9599         
9600         var feedback = this.el.select('.form-control-feedback', true).first();
9601             
9602         if(feedback){
9603             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9604         }
9605
9606         if(this.disabled){
9607             return;
9608         }
9609         
9610         if(this.allowBlank && !this.getRawValue().length){
9611             return;
9612         }
9613         
9614         if(this.indicator){
9615             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9616             this.indicator.addClass('visible');
9617         }
9618         
9619         this.el.addClass(this.invalidClass);
9620         
9621         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9622             
9623             var feedback = this.el.select('.form-control-feedback', true).first();
9624             
9625             if(feedback){
9626                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9627                 
9628                 if(this.getValue().length || this.forceFeedback){
9629                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9630                 }
9631                 
9632             }
9633             
9634         }
9635         
9636         this.fireEvent('invalid', this, msg);
9637     },
9638     // private
9639     SafariOnKeyDown : function(event)
9640     {
9641         // this is a workaround for a password hang bug on chrome/ webkit.
9642         if (this.inputEl().dom.type != 'password') {
9643             return;
9644         }
9645         
9646         var isSelectAll = false;
9647         
9648         if(this.inputEl().dom.selectionEnd > 0){
9649             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9650         }
9651         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9652             event.preventDefault();
9653             this.setValue('');
9654             return;
9655         }
9656         
9657         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9658             
9659             event.preventDefault();
9660             // this is very hacky as keydown always get's upper case.
9661             //
9662             var cc = String.fromCharCode(event.getCharCode());
9663             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9664             
9665         }
9666     },
9667     adjustWidth : function(tag, w){
9668         tag = tag.toLowerCase();
9669         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9670             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9671                 if(tag == 'input'){
9672                     return w + 2;
9673                 }
9674                 if(tag == 'textarea'){
9675                     return w-2;
9676                 }
9677             }else if(Roo.isOpera){
9678                 if(tag == 'input'){
9679                     return w + 2;
9680                 }
9681                 if(tag == 'textarea'){
9682                     return w-2;
9683                 }
9684             }
9685         }
9686         return w;
9687     },
9688     
9689     setFieldLabel : function(v)
9690     {
9691         if(!this.rendered){
9692             return;
9693         }
9694         
9695         if(this.indicator){
9696             var ar = this.el.select('label > span',true);
9697             
9698             if (ar.elements.length) {
9699                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9700                 this.fieldLabel = v;
9701                 return;
9702             }
9703             
9704             var br = this.el.select('label',true);
9705             
9706             if(br.elements.length) {
9707                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9708                 this.fieldLabel = v;
9709                 return;
9710             }
9711             
9712             Roo.log('Cannot Found any of label > span || label in input');
9713             return;
9714         }
9715         
9716         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9717         this.fieldLabel = v;
9718         
9719         
9720     }
9721 });
9722
9723  
9724 /*
9725  * - LGPL
9726  *
9727  * Input
9728  * 
9729  */
9730
9731 /**
9732  * @class Roo.bootstrap.TextArea
9733  * @extends Roo.bootstrap.Input
9734  * Bootstrap TextArea class
9735  * @cfg {Number} cols Specifies the visible width of a text area
9736  * @cfg {Number} rows Specifies the visible number of lines in a text area
9737  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9738  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9739  * @cfg {string} html text
9740  * 
9741  * @constructor
9742  * Create a new TextArea
9743  * @param {Object} config The config object
9744  */
9745
9746 Roo.bootstrap.TextArea = function(config){
9747     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9748    
9749 };
9750
9751 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9752      
9753     cols : false,
9754     rows : 5,
9755     readOnly : false,
9756     warp : 'soft',
9757     resize : false,
9758     value: false,
9759     html: false,
9760     
9761     getAutoCreate : function(){
9762         
9763         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9764         
9765         var id = Roo.id();
9766         
9767         var cfg = {};
9768         
9769         if(this.inputType != 'hidden'){
9770             cfg.cls = 'form-group' //input-group
9771         }
9772         
9773         var input =  {
9774             tag: 'textarea',
9775             id : id,
9776             warp : this.warp,
9777             rows : this.rows,
9778             value : this.value || '',
9779             html: this.html || '',
9780             cls : 'form-control',
9781             placeholder : this.placeholder || '' 
9782             
9783         };
9784         
9785         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9786             input.maxLength = this.maxLength;
9787         }
9788         
9789         if(this.resize){
9790             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9791         }
9792         
9793         if(this.cols){
9794             input.cols = this.cols;
9795         }
9796         
9797         if (this.readOnly) {
9798             input.readonly = true;
9799         }
9800         
9801         if (this.name) {
9802             input.name = this.name;
9803         }
9804         
9805         if (this.size) {
9806             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9807         }
9808         
9809         var settings=this;
9810         ['xs','sm','md','lg'].map(function(size){
9811             if (settings[size]) {
9812                 cfg.cls += ' col-' + size + '-' + settings[size];
9813             }
9814         });
9815         
9816         var inputblock = input;
9817         
9818         if(this.hasFeedback && !this.allowBlank){
9819             
9820             var feedback = {
9821                 tag: 'span',
9822                 cls: 'glyphicon form-control-feedback'
9823             };
9824
9825             inputblock = {
9826                 cls : 'has-feedback',
9827                 cn :  [
9828                     input,
9829                     feedback
9830                 ] 
9831             };  
9832         }
9833         
9834         
9835         if (this.before || this.after) {
9836             
9837             inputblock = {
9838                 cls : 'input-group',
9839                 cn :  [] 
9840             };
9841             if (this.before) {
9842                 inputblock.cn.push({
9843                     tag :'span',
9844                     cls : 'input-group-addon',
9845                     html : this.before
9846                 });
9847             }
9848             
9849             inputblock.cn.push(input);
9850             
9851             if(this.hasFeedback && !this.allowBlank){
9852                 inputblock.cls += ' has-feedback';
9853                 inputblock.cn.push(feedback);
9854             }
9855             
9856             if (this.after) {
9857                 inputblock.cn.push({
9858                     tag :'span',
9859                     cls : 'input-group-addon',
9860                     html : this.after
9861                 });
9862             }
9863             
9864         }
9865         
9866         if (align ==='left' && this.fieldLabel.length) {
9867             cfg.cn = [
9868                 {
9869                     tag: 'label',
9870                     'for' :  id,
9871                     cls : 'control-label',
9872                     html : this.fieldLabel
9873                 },
9874                 {
9875                     cls : "",
9876                     cn: [
9877                         inputblock
9878                     ]
9879                 }
9880
9881             ];
9882             
9883             if(this.labelWidth > 12){
9884                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9885             }
9886
9887             if(this.labelWidth < 13 && this.labelmd == 0){
9888                 this.labelmd = this.labelWidth;
9889             }
9890
9891             if(this.labellg > 0){
9892                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9893                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9894             }
9895
9896             if(this.labelmd > 0){
9897                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9898                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9899             }
9900
9901             if(this.labelsm > 0){
9902                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9903                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9904             }
9905
9906             if(this.labelxs > 0){
9907                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9908                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9909             }
9910             
9911         } else if ( this.fieldLabel.length) {
9912             cfg.cn = [
9913
9914                {
9915                    tag: 'label',
9916                    //cls : 'input-group-addon',
9917                    html : this.fieldLabel
9918
9919                },
9920
9921                inputblock
9922
9923            ];
9924
9925         } else {
9926
9927             cfg.cn = [
9928
9929                 inputblock
9930
9931             ];
9932                 
9933         }
9934         
9935         if (this.disabled) {
9936             input.disabled=true;
9937         }
9938         
9939         return cfg;
9940         
9941     },
9942     /**
9943      * return the real textarea element.
9944      */
9945     inputEl: function ()
9946     {
9947         return this.el.select('textarea.form-control',true).first();
9948     },
9949     
9950     /**
9951      * Clear any invalid styles/messages for this field
9952      */
9953     clearInvalid : function()
9954     {
9955         
9956         if(!this.el || this.preventMark){ // not rendered
9957             return;
9958         }
9959         
9960         var label = this.el.select('label', true).first();
9961         var icon = this.el.select('i.fa-star', true).first();
9962         
9963         if(label && icon){
9964             icon.remove();
9965         }
9966         
9967         this.el.removeClass(this.invalidClass);
9968         
9969         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9970             
9971             var feedback = this.el.select('.form-control-feedback', true).first();
9972             
9973             if(feedback){
9974                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9975             }
9976             
9977         }
9978         
9979         this.fireEvent('valid', this);
9980     },
9981     
9982      /**
9983      * Mark this field as valid
9984      */
9985     markValid : function()
9986     {
9987         if(!this.el  || this.preventMark){ // not rendered
9988             return;
9989         }
9990         
9991         this.el.removeClass([this.invalidClass, this.validClass]);
9992         
9993         var feedback = this.el.select('.form-control-feedback', true).first();
9994             
9995         if(feedback){
9996             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9997         }
9998
9999         if(this.disabled || this.allowBlank){
10000             return;
10001         }
10002         
10003         var label = this.el.select('label', true).first();
10004         var icon = this.el.select('i.fa-star', true).first();
10005         
10006         if(label && icon){
10007             icon.remove();
10008         }
10009         
10010         this.el.addClass(this.validClass);
10011         
10012         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10013             
10014             var feedback = this.el.select('.form-control-feedback', true).first();
10015             
10016             if(feedback){
10017                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10018                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10019             }
10020             
10021         }
10022         
10023         this.fireEvent('valid', this);
10024     },
10025     
10026      /**
10027      * Mark this field as invalid
10028      * @param {String} msg The validation message
10029      */
10030     markInvalid : function(msg)
10031     {
10032         if(!this.el  || this.preventMark){ // not rendered
10033             return;
10034         }
10035         
10036         this.el.removeClass([this.invalidClass, this.validClass]);
10037         
10038         var feedback = this.el.select('.form-control-feedback', true).first();
10039             
10040         if(feedback){
10041             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10042         }
10043
10044         if(this.disabled || this.allowBlank){
10045             return;
10046         }
10047         
10048         var label = this.el.select('label', true).first();
10049         var icon = this.el.select('i.fa-star', true).first();
10050         
10051         if(!this.getValue().length && label && !icon){
10052             this.el.createChild({
10053                 tag : 'i',
10054                 cls : 'text-danger fa fa-lg fa-star',
10055                 tooltip : 'This field is required',
10056                 style : 'margin-right:5px;'
10057             }, label, true);
10058         }
10059
10060         this.el.addClass(this.invalidClass);
10061         
10062         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10063             
10064             var feedback = this.el.select('.form-control-feedback', true).first();
10065             
10066             if(feedback){
10067                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10068                 
10069                 if(this.getValue().length || this.forceFeedback){
10070                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10071                 }
10072                 
10073             }
10074             
10075         }
10076         
10077         this.fireEvent('invalid', this, msg);
10078     }
10079 });
10080
10081  
10082 /*
10083  * - LGPL
10084  *
10085  * trigger field - base class for combo..
10086  * 
10087  */
10088  
10089 /**
10090  * @class Roo.bootstrap.TriggerField
10091  * @extends Roo.bootstrap.Input
10092  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10093  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10094  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10095  * for which you can provide a custom implementation.  For example:
10096  * <pre><code>
10097 var trigger = new Roo.bootstrap.TriggerField();
10098 trigger.onTriggerClick = myTriggerFn;
10099 trigger.applyTo('my-field');
10100 </code></pre>
10101  *
10102  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10103  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10104  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10105  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10106  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10107
10108  * @constructor
10109  * Create a new TriggerField.
10110  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10111  * to the base TextField)
10112  */
10113 Roo.bootstrap.TriggerField = function(config){
10114     this.mimicing = false;
10115     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10116 };
10117
10118 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10119     /**
10120      * @cfg {String} triggerClass A CSS class to apply to the trigger
10121      */
10122      /**
10123      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10124      */
10125     hideTrigger:false,
10126
10127     /**
10128      * @cfg {Boolean} removable (true|false) special filter default false
10129      */
10130     removable : false,
10131     
10132     /** @cfg {Boolean} grow @hide */
10133     /** @cfg {Number} growMin @hide */
10134     /** @cfg {Number} growMax @hide */
10135
10136     /**
10137      * @hide 
10138      * @method
10139      */
10140     autoSize: Roo.emptyFn,
10141     // private
10142     monitorTab : true,
10143     // private
10144     deferHeight : true,
10145
10146     
10147     actionMode : 'wrap',
10148     
10149     caret : false,
10150     
10151     
10152     getAutoCreate : function(){
10153        
10154         var align = this.labelAlign || this.parentLabelAlign();
10155         
10156         var id = Roo.id();
10157         
10158         var cfg = {
10159             cls: 'form-group' //input-group
10160         };
10161         
10162         
10163         var input =  {
10164             tag: 'input',
10165             id : id,
10166             type : this.inputType,
10167             cls : 'form-control',
10168             autocomplete: 'new-password',
10169             placeholder : this.placeholder || '' 
10170             
10171         };
10172         if (this.name) {
10173             input.name = this.name;
10174         }
10175         if (this.size) {
10176             input.cls += ' input-' + this.size;
10177         }
10178         
10179         if (this.disabled) {
10180             input.disabled=true;
10181         }
10182         
10183         var inputblock = input;
10184         
10185         if(this.hasFeedback && !this.allowBlank){
10186             
10187             var feedback = {
10188                 tag: 'span',
10189                 cls: 'glyphicon form-control-feedback'
10190             };
10191             
10192             if(this.removable && !this.editable && !this.tickable){
10193                 inputblock = {
10194                     cls : 'has-feedback',
10195                     cn :  [
10196                         inputblock,
10197                         {
10198                             tag: 'button',
10199                             html : 'x',
10200                             cls : 'roo-combo-removable-btn close'
10201                         },
10202                         feedback
10203                     ] 
10204                 };
10205             } else {
10206                 inputblock = {
10207                     cls : 'has-feedback',
10208                     cn :  [
10209                         inputblock,
10210                         feedback
10211                     ] 
10212                 };
10213             }
10214
10215         } else {
10216             if(this.removable && !this.editable && !this.tickable){
10217                 inputblock = {
10218                     cls : 'roo-removable',
10219                     cn :  [
10220                         inputblock,
10221                         {
10222                             tag: 'button',
10223                             html : 'x',
10224                             cls : 'roo-combo-removable-btn close'
10225                         }
10226                     ] 
10227                 };
10228             }
10229         }
10230         
10231         if (this.before || this.after) {
10232             
10233             inputblock = {
10234                 cls : 'input-group',
10235                 cn :  [] 
10236             };
10237             if (this.before) {
10238                 inputblock.cn.push({
10239                     tag :'span',
10240                     cls : 'input-group-addon',
10241                     html : this.before
10242                 });
10243             }
10244             
10245             inputblock.cn.push(input);
10246             
10247             if(this.hasFeedback && !this.allowBlank){
10248                 inputblock.cls += ' has-feedback';
10249                 inputblock.cn.push(feedback);
10250             }
10251             
10252             if (this.after) {
10253                 inputblock.cn.push({
10254                     tag :'span',
10255                     cls : 'input-group-addon',
10256                     html : this.after
10257                 });
10258             }
10259             
10260         };
10261         
10262         var box = {
10263             tag: 'div',
10264             cn: [
10265                 {
10266                     tag: 'input',
10267                     type : 'hidden',
10268                     cls: 'form-hidden-field'
10269                 },
10270                 inputblock
10271             ]
10272             
10273         };
10274         
10275         if(this.multiple){
10276             box = {
10277                 tag: 'div',
10278                 cn: [
10279                     {
10280                         tag: 'input',
10281                         type : 'hidden',
10282                         cls: 'form-hidden-field'
10283                     },
10284                     {
10285                         tag: 'ul',
10286                         cls: 'roo-select2-choices',
10287                         cn:[
10288                             {
10289                                 tag: 'li',
10290                                 cls: 'roo-select2-search-field',
10291                                 cn: [
10292
10293                                     inputblock
10294                                 ]
10295                             }
10296                         ]
10297                     }
10298                 ]
10299             }
10300         };
10301         
10302         var combobox = {
10303             cls: 'roo-select2-container input-group',
10304             cn: [
10305                 box
10306 //                {
10307 //                    tag: 'ul',
10308 //                    cls: 'typeahead typeahead-long dropdown-menu',
10309 //                    style: 'display:none'
10310 //                }
10311             ]
10312         };
10313         
10314         if(!this.multiple && this.showToggleBtn){
10315             
10316             var caret = {
10317                         tag: 'span',
10318                         cls: 'caret'
10319              };
10320             if (this.caret != false) {
10321                 caret = {
10322                      tag: 'i',
10323                      cls: 'fa fa-' + this.caret
10324                 };
10325                 
10326             }
10327             
10328             combobox.cn.push({
10329                 tag :'span',
10330                 cls : 'input-group-addon btn dropdown-toggle',
10331                 cn : [
10332                     caret,
10333                     {
10334                         tag: 'span',
10335                         cls: 'combobox-clear',
10336                         cn  : [
10337                             {
10338                                 tag : 'i',
10339                                 cls: 'icon-remove'
10340                             }
10341                         ]
10342                     }
10343                 ]
10344
10345             })
10346         }
10347         
10348         if(this.multiple){
10349             combobox.cls += ' roo-select2-container-multi';
10350         }
10351         
10352         if (align ==='left' && this.fieldLabel.length) {
10353             
10354             cfg.cls += ' roo-form-group-label-left';
10355
10356             cfg.cn = [
10357                 {
10358                     tag : 'i',
10359                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10360                     tooltip : 'This field is required'
10361                 },
10362                 {
10363                     tag: 'label',
10364                     'for' :  id,
10365                     cls : 'control-label',
10366                     html : this.fieldLabel
10367
10368                 },
10369                 {
10370                     cls : "", 
10371                     cn: [
10372                         combobox
10373                     ]
10374                 }
10375
10376             ];
10377             
10378             var labelCfg = cfg.cn[1];
10379             var contentCfg = cfg.cn[2];
10380             
10381             if(this.indicatorpos == 'right'){
10382                 cfg.cn = [
10383                     {
10384                         tag: 'label',
10385                         'for' :  id,
10386                         cls : 'control-label',
10387                         cn : [
10388                             {
10389                                 tag : 'span',
10390                                 html : this.fieldLabel
10391                             },
10392                             {
10393                                 tag : 'i',
10394                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10395                                 tooltip : 'This field is required'
10396                             }
10397                         ]
10398                     },
10399                     {
10400                         cls : "", 
10401                         cn: [
10402                             combobox
10403                         ]
10404                     }
10405
10406                 ];
10407                 
10408                 labelCfg = cfg.cn[0];
10409                 contentCfg = cfg.cn[1];
10410             }
10411             
10412             if(this.labelWidth > 12){
10413                 labelCfg.style = "width: " + this.labelWidth + 'px';
10414             }
10415             
10416             if(this.labelWidth < 13 && this.labelmd == 0){
10417                 this.labelmd = this.labelWidth;
10418             }
10419             
10420             if(this.labellg > 0){
10421                 labelCfg.cls += ' col-lg-' + this.labellg;
10422                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10423             }
10424             
10425             if(this.labelmd > 0){
10426                 labelCfg.cls += ' col-md-' + this.labelmd;
10427                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10428             }
10429             
10430             if(this.labelsm > 0){
10431                 labelCfg.cls += ' col-sm-' + this.labelsm;
10432                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10433             }
10434             
10435             if(this.labelxs > 0){
10436                 labelCfg.cls += ' col-xs-' + this.labelxs;
10437                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10438             }
10439             
10440         } else if ( this.fieldLabel.length) {
10441 //                Roo.log(" label");
10442             cfg.cn = [
10443                 {
10444                    tag : 'i',
10445                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10446                    tooltip : 'This field is required'
10447                },
10448                {
10449                    tag: 'label',
10450                    //cls : 'input-group-addon',
10451                    html : this.fieldLabel
10452
10453                },
10454
10455                combobox
10456
10457             ];
10458             
10459             if(this.indicatorpos == 'right'){
10460                 
10461                 cfg.cn = [
10462                     {
10463                        tag: 'label',
10464                        cn : [
10465                            {
10466                                tag : 'span',
10467                                html : this.fieldLabel
10468                            },
10469                            {
10470                               tag : 'i',
10471                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10472                               tooltip : 'This field is required'
10473                            }
10474                        ]
10475
10476                     },
10477                     combobox
10478
10479                 ];
10480
10481             }
10482
10483         } else {
10484             
10485 //                Roo.log(" no label && no align");
10486                 cfg = combobox
10487                      
10488                 
10489         }
10490         
10491         var settings=this;
10492         ['xs','sm','md','lg'].map(function(size){
10493             if (settings[size]) {
10494                 cfg.cls += ' col-' + size + '-' + settings[size];
10495             }
10496         });
10497         
10498         return cfg;
10499         
10500     },
10501     
10502     
10503     
10504     // private
10505     onResize : function(w, h){
10506 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10507 //        if(typeof w == 'number'){
10508 //            var x = w - this.trigger.getWidth();
10509 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10510 //            this.trigger.setStyle('left', x+'px');
10511 //        }
10512     },
10513
10514     // private
10515     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10516
10517     // private
10518     getResizeEl : function(){
10519         return this.inputEl();
10520     },
10521
10522     // private
10523     getPositionEl : function(){
10524         return this.inputEl();
10525     },
10526
10527     // private
10528     alignErrorIcon : function(){
10529         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10530     },
10531
10532     // private
10533     initEvents : function(){
10534         
10535         this.createList();
10536         
10537         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10538         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10539         if(!this.multiple && this.showToggleBtn){
10540             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10541             if(this.hideTrigger){
10542                 this.trigger.setDisplayed(false);
10543             }
10544             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10545         }
10546         
10547         if(this.multiple){
10548             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10549         }
10550         
10551         if(this.removable && !this.editable && !this.tickable){
10552             var close = this.closeTriggerEl();
10553             
10554             if(close){
10555                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10556                 close.on('click', this.removeBtnClick, this, close);
10557             }
10558         }
10559         
10560         //this.trigger.addClassOnOver('x-form-trigger-over');
10561         //this.trigger.addClassOnClick('x-form-trigger-click');
10562         
10563         //if(!this.width){
10564         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10565         //}
10566     },
10567     
10568     closeTriggerEl : function()
10569     {
10570         var close = this.el.select('.roo-combo-removable-btn', true).first();
10571         return close ? close : false;
10572     },
10573     
10574     removeBtnClick : function(e, h, el)
10575     {
10576         e.preventDefault();
10577         
10578         if(this.fireEvent("remove", this) !== false){
10579             this.reset();
10580             this.fireEvent("afterremove", this)
10581         }
10582     },
10583     
10584     createList : function()
10585     {
10586         this.list = Roo.get(document.body).createChild({
10587             tag: 'ul',
10588             cls: 'typeahead typeahead-long dropdown-menu',
10589             style: 'display:none'
10590         });
10591         
10592         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10593         
10594     },
10595
10596     // private
10597     initTrigger : function(){
10598        
10599     },
10600
10601     // private
10602     onDestroy : function(){
10603         if(this.trigger){
10604             this.trigger.removeAllListeners();
10605           //  this.trigger.remove();
10606         }
10607         //if(this.wrap){
10608         //    this.wrap.remove();
10609         //}
10610         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10611     },
10612
10613     // private
10614     onFocus : function(){
10615         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10616         /*
10617         if(!this.mimicing){
10618             this.wrap.addClass('x-trigger-wrap-focus');
10619             this.mimicing = true;
10620             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10621             if(this.monitorTab){
10622                 this.el.on("keydown", this.checkTab, this);
10623             }
10624         }
10625         */
10626     },
10627
10628     // private
10629     checkTab : function(e){
10630         if(e.getKey() == e.TAB){
10631             this.triggerBlur();
10632         }
10633     },
10634
10635     // private
10636     onBlur : function(){
10637         // do nothing
10638     },
10639
10640     // private
10641     mimicBlur : function(e, t){
10642         /*
10643         if(!this.wrap.contains(t) && this.validateBlur()){
10644             this.triggerBlur();
10645         }
10646         */
10647     },
10648
10649     // private
10650     triggerBlur : function(){
10651         this.mimicing = false;
10652         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10653         if(this.monitorTab){
10654             this.el.un("keydown", this.checkTab, this);
10655         }
10656         //this.wrap.removeClass('x-trigger-wrap-focus');
10657         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10658     },
10659
10660     // private
10661     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10662     validateBlur : function(e, t){
10663         return true;
10664     },
10665
10666     // private
10667     onDisable : function(){
10668         this.inputEl().dom.disabled = true;
10669         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10670         //if(this.wrap){
10671         //    this.wrap.addClass('x-item-disabled');
10672         //}
10673     },
10674
10675     // private
10676     onEnable : function(){
10677         this.inputEl().dom.disabled = false;
10678         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10679         //if(this.wrap){
10680         //    this.el.removeClass('x-item-disabled');
10681         //}
10682     },
10683
10684     // private
10685     onShow : function(){
10686         var ae = this.getActionEl();
10687         
10688         if(ae){
10689             ae.dom.style.display = '';
10690             ae.dom.style.visibility = 'visible';
10691         }
10692     },
10693
10694     // private
10695     
10696     onHide : function(){
10697         var ae = this.getActionEl();
10698         ae.dom.style.display = 'none';
10699     },
10700
10701     /**
10702      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10703      * by an implementing function.
10704      * @method
10705      * @param {EventObject} e
10706      */
10707     onTriggerClick : Roo.emptyFn
10708 });
10709  /*
10710  * Based on:
10711  * Ext JS Library 1.1.1
10712  * Copyright(c) 2006-2007, Ext JS, LLC.
10713  *
10714  * Originally Released Under LGPL - original licence link has changed is not relivant.
10715  *
10716  * Fork - LGPL
10717  * <script type="text/javascript">
10718  */
10719
10720
10721 /**
10722  * @class Roo.data.SortTypes
10723  * @singleton
10724  * Defines the default sorting (casting?) comparison functions used when sorting data.
10725  */
10726 Roo.data.SortTypes = {
10727     /**
10728      * Default sort that does nothing
10729      * @param {Mixed} s The value being converted
10730      * @return {Mixed} The comparison value
10731      */
10732     none : function(s){
10733         return s;
10734     },
10735     
10736     /**
10737      * The regular expression used to strip tags
10738      * @type {RegExp}
10739      * @property
10740      */
10741     stripTagsRE : /<\/?[^>]+>/gi,
10742     
10743     /**
10744      * Strips all HTML tags to sort on text only
10745      * @param {Mixed} s The value being converted
10746      * @return {String} The comparison value
10747      */
10748     asText : function(s){
10749         return String(s).replace(this.stripTagsRE, "");
10750     },
10751     
10752     /**
10753      * Strips all HTML tags to sort on text only - Case insensitive
10754      * @param {Mixed} s The value being converted
10755      * @return {String} The comparison value
10756      */
10757     asUCText : function(s){
10758         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10759     },
10760     
10761     /**
10762      * Case insensitive string
10763      * @param {Mixed} s The value being converted
10764      * @return {String} The comparison value
10765      */
10766     asUCString : function(s) {
10767         return String(s).toUpperCase();
10768     },
10769     
10770     /**
10771      * Date sorting
10772      * @param {Mixed} s The value being converted
10773      * @return {Number} The comparison value
10774      */
10775     asDate : function(s) {
10776         if(!s){
10777             return 0;
10778         }
10779         if(s instanceof Date){
10780             return s.getTime();
10781         }
10782         return Date.parse(String(s));
10783     },
10784     
10785     /**
10786      * Float sorting
10787      * @param {Mixed} s The value being converted
10788      * @return {Float} The comparison value
10789      */
10790     asFloat : function(s) {
10791         var val = parseFloat(String(s).replace(/,/g, ""));
10792         if(isNaN(val)) {
10793             val = 0;
10794         }
10795         return val;
10796     },
10797     
10798     /**
10799      * Integer sorting
10800      * @param {Mixed} s The value being converted
10801      * @return {Number} The comparison value
10802      */
10803     asInt : function(s) {
10804         var val = parseInt(String(s).replace(/,/g, ""));
10805         if(isNaN(val)) {
10806             val = 0;
10807         }
10808         return val;
10809     }
10810 };/*
10811  * Based on:
10812  * Ext JS Library 1.1.1
10813  * Copyright(c) 2006-2007, Ext JS, LLC.
10814  *
10815  * Originally Released Under LGPL - original licence link has changed is not relivant.
10816  *
10817  * Fork - LGPL
10818  * <script type="text/javascript">
10819  */
10820
10821 /**
10822 * @class Roo.data.Record
10823  * Instances of this class encapsulate both record <em>definition</em> information, and record
10824  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10825  * to access Records cached in an {@link Roo.data.Store} object.<br>
10826  * <p>
10827  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10828  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10829  * objects.<br>
10830  * <p>
10831  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10832  * @constructor
10833  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10834  * {@link #create}. The parameters are the same.
10835  * @param {Array} data An associative Array of data values keyed by the field name.
10836  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10837  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10838  * not specified an integer id is generated.
10839  */
10840 Roo.data.Record = function(data, id){
10841     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10842     this.data = data;
10843 };
10844
10845 /**
10846  * Generate a constructor for a specific record layout.
10847  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10848  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10849  * Each field definition object may contain the following properties: <ul>
10850  * <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,
10851  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10852  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10853  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10854  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10855  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10856  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10857  * this may be omitted.</p></li>
10858  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10859  * <ul><li>auto (Default, implies no conversion)</li>
10860  * <li>string</li>
10861  * <li>int</li>
10862  * <li>float</li>
10863  * <li>boolean</li>
10864  * <li>date</li></ul></p></li>
10865  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10866  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10867  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10868  * by the Reader into an object that will be stored in the Record. It is passed the
10869  * following parameters:<ul>
10870  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10871  * </ul></p></li>
10872  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10873  * </ul>
10874  * <br>usage:<br><pre><code>
10875 var TopicRecord = Roo.data.Record.create(
10876     {name: 'title', mapping: 'topic_title'},
10877     {name: 'author', mapping: 'username'},
10878     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10879     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10880     {name: 'lastPoster', mapping: 'user2'},
10881     {name: 'excerpt', mapping: 'post_text'}
10882 );
10883
10884 var myNewRecord = new TopicRecord({
10885     title: 'Do my job please',
10886     author: 'noobie',
10887     totalPosts: 1,
10888     lastPost: new Date(),
10889     lastPoster: 'Animal',
10890     excerpt: 'No way dude!'
10891 });
10892 myStore.add(myNewRecord);
10893 </code></pre>
10894  * @method create
10895  * @static
10896  */
10897 Roo.data.Record.create = function(o){
10898     var f = function(){
10899         f.superclass.constructor.apply(this, arguments);
10900     };
10901     Roo.extend(f, Roo.data.Record);
10902     var p = f.prototype;
10903     p.fields = new Roo.util.MixedCollection(false, function(field){
10904         return field.name;
10905     });
10906     for(var i = 0, len = o.length; i < len; i++){
10907         p.fields.add(new Roo.data.Field(o[i]));
10908     }
10909     f.getField = function(name){
10910         return p.fields.get(name);  
10911     };
10912     return f;
10913 };
10914
10915 Roo.data.Record.AUTO_ID = 1000;
10916 Roo.data.Record.EDIT = 'edit';
10917 Roo.data.Record.REJECT = 'reject';
10918 Roo.data.Record.COMMIT = 'commit';
10919
10920 Roo.data.Record.prototype = {
10921     /**
10922      * Readonly flag - true if this record has been modified.
10923      * @type Boolean
10924      */
10925     dirty : false,
10926     editing : false,
10927     error: null,
10928     modified: null,
10929
10930     // private
10931     join : function(store){
10932         this.store = store;
10933     },
10934
10935     /**
10936      * Set the named field to the specified value.
10937      * @param {String} name The name of the field to set.
10938      * @param {Object} value The value to set the field to.
10939      */
10940     set : function(name, value){
10941         if(this.data[name] == value){
10942             return;
10943         }
10944         this.dirty = true;
10945         if(!this.modified){
10946             this.modified = {};
10947         }
10948         if(typeof this.modified[name] == 'undefined'){
10949             this.modified[name] = this.data[name];
10950         }
10951         this.data[name] = value;
10952         if(!this.editing && this.store){
10953             this.store.afterEdit(this);
10954         }       
10955     },
10956
10957     /**
10958      * Get the value of the named field.
10959      * @param {String} name The name of the field to get the value of.
10960      * @return {Object} The value of the field.
10961      */
10962     get : function(name){
10963         return this.data[name]; 
10964     },
10965
10966     // private
10967     beginEdit : function(){
10968         this.editing = true;
10969         this.modified = {}; 
10970     },
10971
10972     // private
10973     cancelEdit : function(){
10974         this.editing = false;
10975         delete this.modified;
10976     },
10977
10978     // private
10979     endEdit : function(){
10980         this.editing = false;
10981         if(this.dirty && this.store){
10982             this.store.afterEdit(this);
10983         }
10984     },
10985
10986     /**
10987      * Usually called by the {@link Roo.data.Store} which owns the Record.
10988      * Rejects all changes made to the Record since either creation, or the last commit operation.
10989      * Modified fields are reverted to their original values.
10990      * <p>
10991      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10992      * of reject operations.
10993      */
10994     reject : function(){
10995         var m = this.modified;
10996         for(var n in m){
10997             if(typeof m[n] != "function"){
10998                 this.data[n] = m[n];
10999             }
11000         }
11001         this.dirty = false;
11002         delete this.modified;
11003         this.editing = false;
11004         if(this.store){
11005             this.store.afterReject(this);
11006         }
11007     },
11008
11009     /**
11010      * Usually called by the {@link Roo.data.Store} which owns the Record.
11011      * Commits all changes made to the Record since either creation, or the last commit operation.
11012      * <p>
11013      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11014      * of commit operations.
11015      */
11016     commit : function(){
11017         this.dirty = false;
11018         delete this.modified;
11019         this.editing = false;
11020         if(this.store){
11021             this.store.afterCommit(this);
11022         }
11023     },
11024
11025     // private
11026     hasError : function(){
11027         return this.error != null;
11028     },
11029
11030     // private
11031     clearError : function(){
11032         this.error = null;
11033     },
11034
11035     /**
11036      * Creates a copy of this record.
11037      * @param {String} id (optional) A new record id if you don't want to use this record's id
11038      * @return {Record}
11039      */
11040     copy : function(newId) {
11041         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11042     }
11043 };/*
11044  * Based on:
11045  * Ext JS Library 1.1.1
11046  * Copyright(c) 2006-2007, Ext JS, LLC.
11047  *
11048  * Originally Released Under LGPL - original licence link has changed is not relivant.
11049  *
11050  * Fork - LGPL
11051  * <script type="text/javascript">
11052  */
11053
11054
11055
11056 /**
11057  * @class Roo.data.Store
11058  * @extends Roo.util.Observable
11059  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11060  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11061  * <p>
11062  * 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
11063  * has no knowledge of the format of the data returned by the Proxy.<br>
11064  * <p>
11065  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11066  * instances from the data object. These records are cached and made available through accessor functions.
11067  * @constructor
11068  * Creates a new Store.
11069  * @param {Object} config A config object containing the objects needed for the Store to access data,
11070  * and read the data into Records.
11071  */
11072 Roo.data.Store = function(config){
11073     this.data = new Roo.util.MixedCollection(false);
11074     this.data.getKey = function(o){
11075         return o.id;
11076     };
11077     this.baseParams = {};
11078     // private
11079     this.paramNames = {
11080         "start" : "start",
11081         "limit" : "limit",
11082         "sort" : "sort",
11083         "dir" : "dir",
11084         "multisort" : "_multisort"
11085     };
11086
11087     if(config && config.data){
11088         this.inlineData = config.data;
11089         delete config.data;
11090     }
11091
11092     Roo.apply(this, config);
11093     
11094     if(this.reader){ // reader passed
11095         this.reader = Roo.factory(this.reader, Roo.data);
11096         this.reader.xmodule = this.xmodule || false;
11097         if(!this.recordType){
11098             this.recordType = this.reader.recordType;
11099         }
11100         if(this.reader.onMetaChange){
11101             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11102         }
11103     }
11104
11105     if(this.recordType){
11106         this.fields = this.recordType.prototype.fields;
11107     }
11108     this.modified = [];
11109
11110     this.addEvents({
11111         /**
11112          * @event datachanged
11113          * Fires when the data cache has changed, and a widget which is using this Store
11114          * as a Record cache should refresh its view.
11115          * @param {Store} this
11116          */
11117         datachanged : true,
11118         /**
11119          * @event metachange
11120          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11121          * @param {Store} this
11122          * @param {Object} meta The JSON metadata
11123          */
11124         metachange : true,
11125         /**
11126          * @event add
11127          * Fires when Records have been added to the Store
11128          * @param {Store} this
11129          * @param {Roo.data.Record[]} records The array of Records added
11130          * @param {Number} index The index at which the record(s) were added
11131          */
11132         add : true,
11133         /**
11134          * @event remove
11135          * Fires when a Record has been removed from the Store
11136          * @param {Store} this
11137          * @param {Roo.data.Record} record The Record that was removed
11138          * @param {Number} index The index at which the record was removed
11139          */
11140         remove : true,
11141         /**
11142          * @event update
11143          * Fires when a Record has been updated
11144          * @param {Store} this
11145          * @param {Roo.data.Record} record The Record that was updated
11146          * @param {String} operation The update operation being performed.  Value may be one of:
11147          * <pre><code>
11148  Roo.data.Record.EDIT
11149  Roo.data.Record.REJECT
11150  Roo.data.Record.COMMIT
11151          * </code></pre>
11152          */
11153         update : true,
11154         /**
11155          * @event clear
11156          * Fires when the data cache has been cleared.
11157          * @param {Store} this
11158          */
11159         clear : true,
11160         /**
11161          * @event beforeload
11162          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11163          * the load action will be canceled.
11164          * @param {Store} this
11165          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11166          */
11167         beforeload : true,
11168         /**
11169          * @event beforeloadadd
11170          * Fires after a new set of Records has been loaded.
11171          * @param {Store} this
11172          * @param {Roo.data.Record[]} records The Records that were loaded
11173          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11174          */
11175         beforeloadadd : true,
11176         /**
11177          * @event load
11178          * Fires after a new set of Records has been loaded, before they are added to the store.
11179          * @param {Store} this
11180          * @param {Roo.data.Record[]} records The Records that were loaded
11181          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11182          * @params {Object} return from reader
11183          */
11184         load : true,
11185         /**
11186          * @event loadexception
11187          * Fires if an exception occurs in the Proxy during loading.
11188          * Called with the signature of the Proxy's "loadexception" event.
11189          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11190          * 
11191          * @param {Proxy} 
11192          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11193          * @param {Object} load options 
11194          * @param {Object} jsonData from your request (normally this contains the Exception)
11195          */
11196         loadexception : true
11197     });
11198     
11199     if(this.proxy){
11200         this.proxy = Roo.factory(this.proxy, Roo.data);
11201         this.proxy.xmodule = this.xmodule || false;
11202         this.relayEvents(this.proxy,  ["loadexception"]);
11203     }
11204     this.sortToggle = {};
11205     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11206
11207     Roo.data.Store.superclass.constructor.call(this);
11208
11209     if(this.inlineData){
11210         this.loadData(this.inlineData);
11211         delete this.inlineData;
11212     }
11213 };
11214
11215 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11216      /**
11217     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11218     * without a remote query - used by combo/forms at present.
11219     */
11220     
11221     /**
11222     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11223     */
11224     /**
11225     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11226     */
11227     /**
11228     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11229     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11230     */
11231     /**
11232     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11233     * on any HTTP request
11234     */
11235     /**
11236     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11237     */
11238     /**
11239     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11240     */
11241     multiSort: false,
11242     /**
11243     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11244     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11245     */
11246     remoteSort : false,
11247
11248     /**
11249     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11250      * loaded or when a record is removed. (defaults to false).
11251     */
11252     pruneModifiedRecords : false,
11253
11254     // private
11255     lastOptions : null,
11256
11257     /**
11258      * Add Records to the Store and fires the add event.
11259      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11260      */
11261     add : function(records){
11262         records = [].concat(records);
11263         for(var i = 0, len = records.length; i < len; i++){
11264             records[i].join(this);
11265         }
11266         var index = this.data.length;
11267         this.data.addAll(records);
11268         this.fireEvent("add", this, records, index);
11269     },
11270
11271     /**
11272      * Remove a Record from the Store and fires the remove event.
11273      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11274      */
11275     remove : function(record){
11276         var index = this.data.indexOf(record);
11277         this.data.removeAt(index);
11278  
11279         if(this.pruneModifiedRecords){
11280             this.modified.remove(record);
11281         }
11282         this.fireEvent("remove", this, record, index);
11283     },
11284
11285     /**
11286      * Remove all Records from the Store and fires the clear event.
11287      */
11288     removeAll : function(){
11289         this.data.clear();
11290         if(this.pruneModifiedRecords){
11291             this.modified = [];
11292         }
11293         this.fireEvent("clear", this);
11294     },
11295
11296     /**
11297      * Inserts Records to the Store at the given index and fires the add event.
11298      * @param {Number} index The start index at which to insert the passed Records.
11299      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11300      */
11301     insert : function(index, records){
11302         records = [].concat(records);
11303         for(var i = 0, len = records.length; i < len; i++){
11304             this.data.insert(index, records[i]);
11305             records[i].join(this);
11306         }
11307         this.fireEvent("add", this, records, index);
11308     },
11309
11310     /**
11311      * Get the index within the cache of the passed Record.
11312      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11313      * @return {Number} The index of the passed Record. Returns -1 if not found.
11314      */
11315     indexOf : function(record){
11316         return this.data.indexOf(record);
11317     },
11318
11319     /**
11320      * Get the index within the cache of the Record with the passed id.
11321      * @param {String} id The id of the Record to find.
11322      * @return {Number} The index of the Record. Returns -1 if not found.
11323      */
11324     indexOfId : function(id){
11325         return this.data.indexOfKey(id);
11326     },
11327
11328     /**
11329      * Get the Record with the specified id.
11330      * @param {String} id The id of the Record to find.
11331      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11332      */
11333     getById : function(id){
11334         return this.data.key(id);
11335     },
11336
11337     /**
11338      * Get the Record at the specified index.
11339      * @param {Number} index The index of the Record to find.
11340      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11341      */
11342     getAt : function(index){
11343         return this.data.itemAt(index);
11344     },
11345
11346     /**
11347      * Returns a range of Records between specified indices.
11348      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11349      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11350      * @return {Roo.data.Record[]} An array of Records
11351      */
11352     getRange : function(start, end){
11353         return this.data.getRange(start, end);
11354     },
11355
11356     // private
11357     storeOptions : function(o){
11358         o = Roo.apply({}, o);
11359         delete o.callback;
11360         delete o.scope;
11361         this.lastOptions = o;
11362     },
11363
11364     /**
11365      * Loads the Record cache from the configured Proxy using the configured Reader.
11366      * <p>
11367      * If using remote paging, then the first load call must specify the <em>start</em>
11368      * and <em>limit</em> properties in the options.params property to establish the initial
11369      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11370      * <p>
11371      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11372      * and this call will return before the new data has been loaded. Perform any post-processing
11373      * in a callback function, or in a "load" event handler.</strong>
11374      * <p>
11375      * @param {Object} options An object containing properties which control loading options:<ul>
11376      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11377      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11378      * passed the following arguments:<ul>
11379      * <li>r : Roo.data.Record[]</li>
11380      * <li>options: Options object from the load call</li>
11381      * <li>success: Boolean success indicator</li></ul></li>
11382      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11383      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11384      * </ul>
11385      */
11386     load : function(options){
11387         options = options || {};
11388         if(this.fireEvent("beforeload", this, options) !== false){
11389             this.storeOptions(options);
11390             var p = Roo.apply(options.params || {}, this.baseParams);
11391             // if meta was not loaded from remote source.. try requesting it.
11392             if (!this.reader.metaFromRemote) {
11393                 p._requestMeta = 1;
11394             }
11395             if(this.sortInfo && this.remoteSort){
11396                 var pn = this.paramNames;
11397                 p[pn["sort"]] = this.sortInfo.field;
11398                 p[pn["dir"]] = this.sortInfo.direction;
11399             }
11400             if (this.multiSort) {
11401                 var pn = this.paramNames;
11402                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11403             }
11404             
11405             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11406         }
11407     },
11408
11409     /**
11410      * Reloads the Record cache from the configured Proxy using the configured Reader and
11411      * the options from the last load operation performed.
11412      * @param {Object} options (optional) An object containing properties which may override the options
11413      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11414      * the most recently used options are reused).
11415      */
11416     reload : function(options){
11417         this.load(Roo.applyIf(options||{}, this.lastOptions));
11418     },
11419
11420     // private
11421     // Called as a callback by the Reader during a load operation.
11422     loadRecords : function(o, options, success){
11423         if(!o || success === false){
11424             if(success !== false){
11425                 this.fireEvent("load", this, [], options, o);
11426             }
11427             if(options.callback){
11428                 options.callback.call(options.scope || this, [], options, false);
11429             }
11430             return;
11431         }
11432         // if data returned failure - throw an exception.
11433         if (o.success === false) {
11434             // show a message if no listener is registered.
11435             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11436                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11437             }
11438             // loadmask wil be hooked into this..
11439             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11440             return;
11441         }
11442         var r = o.records, t = o.totalRecords || r.length;
11443         
11444         this.fireEvent("beforeloadadd", this, r, options, o);
11445         
11446         if(!options || options.add !== true){
11447             if(this.pruneModifiedRecords){
11448                 this.modified = [];
11449             }
11450             for(var i = 0, len = r.length; i < len; i++){
11451                 r[i].join(this);
11452             }
11453             if(this.snapshot){
11454                 this.data = this.snapshot;
11455                 delete this.snapshot;
11456             }
11457             this.data.clear();
11458             this.data.addAll(r);
11459             this.totalLength = t;
11460             this.applySort();
11461             this.fireEvent("datachanged", this);
11462         }else{
11463             this.totalLength = Math.max(t, this.data.length+r.length);
11464             this.add(r);
11465         }
11466         
11467         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11468                 
11469             var e = new Roo.data.Record({});
11470
11471             e.set(this.parent.displayField, this.parent.emptyTitle);
11472             e.set(this.parent.valueField, '');
11473
11474             this.insert(0, e);
11475         }
11476             
11477         this.fireEvent("load", this, r, options, o);
11478         if(options.callback){
11479             options.callback.call(options.scope || this, r, options, true);
11480         }
11481     },
11482
11483
11484     /**
11485      * Loads data from a passed data block. A Reader which understands the format of the data
11486      * must have been configured in the constructor.
11487      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11488      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11489      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11490      */
11491     loadData : function(o, append){
11492         var r = this.reader.readRecords(o);
11493         this.loadRecords(r, {add: append}, true);
11494     },
11495
11496     /**
11497      * Gets the number of cached records.
11498      * <p>
11499      * <em>If using paging, this may not be the total size of the dataset. If the data object
11500      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11501      * the data set size</em>
11502      */
11503     getCount : function(){
11504         return this.data.length || 0;
11505     },
11506
11507     /**
11508      * Gets the total number of records in the dataset as returned by the server.
11509      * <p>
11510      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11511      * the dataset size</em>
11512      */
11513     getTotalCount : function(){
11514         return this.totalLength || 0;
11515     },
11516
11517     /**
11518      * Returns the sort state of the Store as an object with two properties:
11519      * <pre><code>
11520  field {String} The name of the field by which the Records are sorted
11521  direction {String} The sort order, "ASC" or "DESC"
11522      * </code></pre>
11523      */
11524     getSortState : function(){
11525         return this.sortInfo;
11526     },
11527
11528     // private
11529     applySort : function(){
11530         if(this.sortInfo && !this.remoteSort){
11531             var s = this.sortInfo, f = s.field;
11532             var st = this.fields.get(f).sortType;
11533             var fn = function(r1, r2){
11534                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11535                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11536             };
11537             this.data.sort(s.direction, fn);
11538             if(this.snapshot && this.snapshot != this.data){
11539                 this.snapshot.sort(s.direction, fn);
11540             }
11541         }
11542     },
11543
11544     /**
11545      * Sets the default sort column and order to be used by the next load operation.
11546      * @param {String} fieldName The name of the field to sort by.
11547      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11548      */
11549     setDefaultSort : function(field, dir){
11550         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11551     },
11552
11553     /**
11554      * Sort the Records.
11555      * If remote sorting is used, the sort is performed on the server, and the cache is
11556      * reloaded. If local sorting is used, the cache is sorted internally.
11557      * @param {String} fieldName The name of the field to sort by.
11558      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11559      */
11560     sort : function(fieldName, dir){
11561         var f = this.fields.get(fieldName);
11562         if(!dir){
11563             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11564             
11565             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11566                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11567             }else{
11568                 dir = f.sortDir;
11569             }
11570         }
11571         this.sortToggle[f.name] = dir;
11572         this.sortInfo = {field: f.name, direction: dir};
11573         if(!this.remoteSort){
11574             this.applySort();
11575             this.fireEvent("datachanged", this);
11576         }else{
11577             this.load(this.lastOptions);
11578         }
11579     },
11580
11581     /**
11582      * Calls the specified function for each of the Records in the cache.
11583      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11584      * Returning <em>false</em> aborts and exits the iteration.
11585      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11586      */
11587     each : function(fn, scope){
11588         this.data.each(fn, scope);
11589     },
11590
11591     /**
11592      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11593      * (e.g., during paging).
11594      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11595      */
11596     getModifiedRecords : function(){
11597         return this.modified;
11598     },
11599
11600     // private
11601     createFilterFn : function(property, value, anyMatch){
11602         if(!value.exec){ // not a regex
11603             value = String(value);
11604             if(value.length == 0){
11605                 return false;
11606             }
11607             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11608         }
11609         return function(r){
11610             return value.test(r.data[property]);
11611         };
11612     },
11613
11614     /**
11615      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11616      * @param {String} property A field on your records
11617      * @param {Number} start The record index to start at (defaults to 0)
11618      * @param {Number} end The last record index to include (defaults to length - 1)
11619      * @return {Number} The sum
11620      */
11621     sum : function(property, start, end){
11622         var rs = this.data.items, v = 0;
11623         start = start || 0;
11624         end = (end || end === 0) ? end : rs.length-1;
11625
11626         for(var i = start; i <= end; i++){
11627             v += (rs[i].data[property] || 0);
11628         }
11629         return v;
11630     },
11631
11632     /**
11633      * Filter the records by a specified property.
11634      * @param {String} field A field on your records
11635      * @param {String/RegExp} value Either a string that the field
11636      * should start with or a RegExp to test against the field
11637      * @param {Boolean} anyMatch True to match any part not just the beginning
11638      */
11639     filter : function(property, value, anyMatch){
11640         var fn = this.createFilterFn(property, value, anyMatch);
11641         return fn ? this.filterBy(fn) : this.clearFilter();
11642     },
11643
11644     /**
11645      * Filter by a function. The specified function will be called with each
11646      * record in this data source. If the function returns true the record is included,
11647      * otherwise it is filtered.
11648      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11649      * @param {Object} scope (optional) The scope of the function (defaults to this)
11650      */
11651     filterBy : function(fn, scope){
11652         this.snapshot = this.snapshot || this.data;
11653         this.data = this.queryBy(fn, scope||this);
11654         this.fireEvent("datachanged", this);
11655     },
11656
11657     /**
11658      * Query the records by a specified property.
11659      * @param {String} field A field on your records
11660      * @param {String/RegExp} value Either a string that the field
11661      * should start with or a RegExp to test against the field
11662      * @param {Boolean} anyMatch True to match any part not just the beginning
11663      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11664      */
11665     query : function(property, value, anyMatch){
11666         var fn = this.createFilterFn(property, value, anyMatch);
11667         return fn ? this.queryBy(fn) : this.data.clone();
11668     },
11669
11670     /**
11671      * Query by a function. The specified function will be called with each
11672      * record in this data source. If the function returns true the record is included
11673      * in the results.
11674      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11675      * @param {Object} scope (optional) The scope of the function (defaults to this)
11676       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11677      **/
11678     queryBy : function(fn, scope){
11679         var data = this.snapshot || this.data;
11680         return data.filterBy(fn, scope||this);
11681     },
11682
11683     /**
11684      * Collects unique values for a particular dataIndex from this store.
11685      * @param {String} dataIndex The property to collect
11686      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11687      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11688      * @return {Array} An array of the unique values
11689      **/
11690     collect : function(dataIndex, allowNull, bypassFilter){
11691         var d = (bypassFilter === true && this.snapshot) ?
11692                 this.snapshot.items : this.data.items;
11693         var v, sv, r = [], l = {};
11694         for(var i = 0, len = d.length; i < len; i++){
11695             v = d[i].data[dataIndex];
11696             sv = String(v);
11697             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11698                 l[sv] = true;
11699                 r[r.length] = v;
11700             }
11701         }
11702         return r;
11703     },
11704
11705     /**
11706      * Revert to a view of the Record cache with no filtering applied.
11707      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11708      */
11709     clearFilter : function(suppressEvent){
11710         if(this.snapshot && this.snapshot != this.data){
11711             this.data = this.snapshot;
11712             delete this.snapshot;
11713             if(suppressEvent !== true){
11714                 this.fireEvent("datachanged", this);
11715             }
11716         }
11717     },
11718
11719     // private
11720     afterEdit : function(record){
11721         if(this.modified.indexOf(record) == -1){
11722             this.modified.push(record);
11723         }
11724         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11725     },
11726     
11727     // private
11728     afterReject : function(record){
11729         this.modified.remove(record);
11730         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11731     },
11732
11733     // private
11734     afterCommit : function(record){
11735         this.modified.remove(record);
11736         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11737     },
11738
11739     /**
11740      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11741      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11742      */
11743     commitChanges : function(){
11744         var m = this.modified.slice(0);
11745         this.modified = [];
11746         for(var i = 0, len = m.length; i < len; i++){
11747             m[i].commit();
11748         }
11749     },
11750
11751     /**
11752      * Cancel outstanding changes on all changed records.
11753      */
11754     rejectChanges : function(){
11755         var m = this.modified.slice(0);
11756         this.modified = [];
11757         for(var i = 0, len = m.length; i < len; i++){
11758             m[i].reject();
11759         }
11760     },
11761
11762     onMetaChange : function(meta, rtype, o){
11763         this.recordType = rtype;
11764         this.fields = rtype.prototype.fields;
11765         delete this.snapshot;
11766         this.sortInfo = meta.sortInfo || this.sortInfo;
11767         this.modified = [];
11768         this.fireEvent('metachange', this, this.reader.meta);
11769     },
11770     
11771     moveIndex : function(data, type)
11772     {
11773         var index = this.indexOf(data);
11774         
11775         var newIndex = index + type;
11776         
11777         this.remove(data);
11778         
11779         this.insert(newIndex, data);
11780         
11781     }
11782 });/*
11783  * Based on:
11784  * Ext JS Library 1.1.1
11785  * Copyright(c) 2006-2007, Ext JS, LLC.
11786  *
11787  * Originally Released Under LGPL - original licence link has changed is not relivant.
11788  *
11789  * Fork - LGPL
11790  * <script type="text/javascript">
11791  */
11792
11793 /**
11794  * @class Roo.data.SimpleStore
11795  * @extends Roo.data.Store
11796  * Small helper class to make creating Stores from Array data easier.
11797  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11798  * @cfg {Array} fields An array of field definition objects, or field name strings.
11799  * @cfg {Array} data The multi-dimensional array of data
11800  * @constructor
11801  * @param {Object} config
11802  */
11803 Roo.data.SimpleStore = function(config){
11804     Roo.data.SimpleStore.superclass.constructor.call(this, {
11805         isLocal : true,
11806         reader: new Roo.data.ArrayReader({
11807                 id: config.id
11808             },
11809             Roo.data.Record.create(config.fields)
11810         ),
11811         proxy : new Roo.data.MemoryProxy(config.data)
11812     });
11813     this.load();
11814 };
11815 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11816  * Based on:
11817  * Ext JS Library 1.1.1
11818  * Copyright(c) 2006-2007, Ext JS, LLC.
11819  *
11820  * Originally Released Under LGPL - original licence link has changed is not relivant.
11821  *
11822  * Fork - LGPL
11823  * <script type="text/javascript">
11824  */
11825
11826 /**
11827 /**
11828  * @extends Roo.data.Store
11829  * @class Roo.data.JsonStore
11830  * Small helper class to make creating Stores for JSON data easier. <br/>
11831 <pre><code>
11832 var store = new Roo.data.JsonStore({
11833     url: 'get-images.php',
11834     root: 'images',
11835     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11836 });
11837 </code></pre>
11838  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11839  * JsonReader and HttpProxy (unless inline data is provided).</b>
11840  * @cfg {Array} fields An array of field definition objects, or field name strings.
11841  * @constructor
11842  * @param {Object} config
11843  */
11844 Roo.data.JsonStore = function(c){
11845     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11846         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11847         reader: new Roo.data.JsonReader(c, c.fields)
11848     }));
11849 };
11850 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11851  * Based on:
11852  * Ext JS Library 1.1.1
11853  * Copyright(c) 2006-2007, Ext JS, LLC.
11854  *
11855  * Originally Released Under LGPL - original licence link has changed is not relivant.
11856  *
11857  * Fork - LGPL
11858  * <script type="text/javascript">
11859  */
11860
11861  
11862 Roo.data.Field = function(config){
11863     if(typeof config == "string"){
11864         config = {name: config};
11865     }
11866     Roo.apply(this, config);
11867     
11868     if(!this.type){
11869         this.type = "auto";
11870     }
11871     
11872     var st = Roo.data.SortTypes;
11873     // named sortTypes are supported, here we look them up
11874     if(typeof this.sortType == "string"){
11875         this.sortType = st[this.sortType];
11876     }
11877     
11878     // set default sortType for strings and dates
11879     if(!this.sortType){
11880         switch(this.type){
11881             case "string":
11882                 this.sortType = st.asUCString;
11883                 break;
11884             case "date":
11885                 this.sortType = st.asDate;
11886                 break;
11887             default:
11888                 this.sortType = st.none;
11889         }
11890     }
11891
11892     // define once
11893     var stripRe = /[\$,%]/g;
11894
11895     // prebuilt conversion function for this field, instead of
11896     // switching every time we're reading a value
11897     if(!this.convert){
11898         var cv, dateFormat = this.dateFormat;
11899         switch(this.type){
11900             case "":
11901             case "auto":
11902             case undefined:
11903                 cv = function(v){ return v; };
11904                 break;
11905             case "string":
11906                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11907                 break;
11908             case "int":
11909                 cv = function(v){
11910                     return v !== undefined && v !== null && v !== '' ?
11911                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11912                     };
11913                 break;
11914             case "float":
11915                 cv = function(v){
11916                     return v !== undefined && v !== null && v !== '' ?
11917                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11918                     };
11919                 break;
11920             case "bool":
11921             case "boolean":
11922                 cv = function(v){ return v === true || v === "true" || v == 1; };
11923                 break;
11924             case "date":
11925                 cv = function(v){
11926                     if(!v){
11927                         return '';
11928                     }
11929                     if(v instanceof Date){
11930                         return v;
11931                     }
11932                     if(dateFormat){
11933                         if(dateFormat == "timestamp"){
11934                             return new Date(v*1000);
11935                         }
11936                         return Date.parseDate(v, dateFormat);
11937                     }
11938                     var parsed = Date.parse(v);
11939                     return parsed ? new Date(parsed) : null;
11940                 };
11941              break;
11942             
11943         }
11944         this.convert = cv;
11945     }
11946 };
11947
11948 Roo.data.Field.prototype = {
11949     dateFormat: null,
11950     defaultValue: "",
11951     mapping: null,
11952     sortType : null,
11953     sortDir : "ASC"
11954 };/*
11955  * Based on:
11956  * Ext JS Library 1.1.1
11957  * Copyright(c) 2006-2007, Ext JS, LLC.
11958  *
11959  * Originally Released Under LGPL - original licence link has changed is not relivant.
11960  *
11961  * Fork - LGPL
11962  * <script type="text/javascript">
11963  */
11964  
11965 // Base class for reading structured data from a data source.  This class is intended to be
11966 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11967
11968 /**
11969  * @class Roo.data.DataReader
11970  * Base class for reading structured data from a data source.  This class is intended to be
11971  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11972  */
11973
11974 Roo.data.DataReader = function(meta, recordType){
11975     
11976     this.meta = meta;
11977     
11978     this.recordType = recordType instanceof Array ? 
11979         Roo.data.Record.create(recordType) : recordType;
11980 };
11981
11982 Roo.data.DataReader.prototype = {
11983      /**
11984      * Create an empty record
11985      * @param {Object} data (optional) - overlay some values
11986      * @return {Roo.data.Record} record created.
11987      */
11988     newRow :  function(d) {
11989         var da =  {};
11990         this.recordType.prototype.fields.each(function(c) {
11991             switch( c.type) {
11992                 case 'int' : da[c.name] = 0; break;
11993                 case 'date' : da[c.name] = new Date(); break;
11994                 case 'float' : da[c.name] = 0.0; break;
11995                 case 'boolean' : da[c.name] = false; break;
11996                 default : da[c.name] = ""; break;
11997             }
11998             
11999         });
12000         return new this.recordType(Roo.apply(da, d));
12001     }
12002     
12003 };/*
12004  * Based on:
12005  * Ext JS Library 1.1.1
12006  * Copyright(c) 2006-2007, Ext JS, LLC.
12007  *
12008  * Originally Released Under LGPL - original licence link has changed is not relivant.
12009  *
12010  * Fork - LGPL
12011  * <script type="text/javascript">
12012  */
12013
12014 /**
12015  * @class Roo.data.DataProxy
12016  * @extends Roo.data.Observable
12017  * This class is an abstract base class for implementations which provide retrieval of
12018  * unformatted data objects.<br>
12019  * <p>
12020  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12021  * (of the appropriate type which knows how to parse the data object) to provide a block of
12022  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12023  * <p>
12024  * Custom implementations must implement the load method as described in
12025  * {@link Roo.data.HttpProxy#load}.
12026  */
12027 Roo.data.DataProxy = function(){
12028     this.addEvents({
12029         /**
12030          * @event beforeload
12031          * Fires before a network request is made to retrieve a data object.
12032          * @param {Object} This DataProxy object.
12033          * @param {Object} params The params parameter to the load function.
12034          */
12035         beforeload : true,
12036         /**
12037          * @event load
12038          * Fires before the load method's callback is called.
12039          * @param {Object} This DataProxy object.
12040          * @param {Object} o The data object.
12041          * @param {Object} arg The callback argument object passed to the load function.
12042          */
12043         load : true,
12044         /**
12045          * @event loadexception
12046          * Fires if an Exception occurs during data retrieval.
12047          * @param {Object} This DataProxy object.
12048          * @param {Object} o The data object.
12049          * @param {Object} arg The callback argument object passed to the load function.
12050          * @param {Object} e The Exception.
12051          */
12052         loadexception : true
12053     });
12054     Roo.data.DataProxy.superclass.constructor.call(this);
12055 };
12056
12057 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12058
12059     /**
12060      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12061      */
12062 /*
12063  * Based on:
12064  * Ext JS Library 1.1.1
12065  * Copyright(c) 2006-2007, Ext JS, LLC.
12066  *
12067  * Originally Released Under LGPL - original licence link has changed is not relivant.
12068  *
12069  * Fork - LGPL
12070  * <script type="text/javascript">
12071  */
12072 /**
12073  * @class Roo.data.MemoryProxy
12074  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12075  * to the Reader when its load method is called.
12076  * @constructor
12077  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12078  */
12079 Roo.data.MemoryProxy = function(data){
12080     if (data.data) {
12081         data = data.data;
12082     }
12083     Roo.data.MemoryProxy.superclass.constructor.call(this);
12084     this.data = data;
12085 };
12086
12087 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12088     
12089     /**
12090      * Load data from the requested source (in this case an in-memory
12091      * data object passed to the constructor), read the data object into
12092      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12093      * process that block using the passed callback.
12094      * @param {Object} params This parameter is not used by the MemoryProxy class.
12095      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12096      * object into a block of Roo.data.Records.
12097      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12098      * The function must be passed <ul>
12099      * <li>The Record block object</li>
12100      * <li>The "arg" argument from the load function</li>
12101      * <li>A boolean success indicator</li>
12102      * </ul>
12103      * @param {Object} scope The scope in which to call the callback
12104      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12105      */
12106     load : function(params, reader, callback, scope, arg){
12107         params = params || {};
12108         var result;
12109         try {
12110             result = reader.readRecords(this.data);
12111         }catch(e){
12112             this.fireEvent("loadexception", this, arg, null, e);
12113             callback.call(scope, null, arg, false);
12114             return;
12115         }
12116         callback.call(scope, result, arg, true);
12117     },
12118     
12119     // private
12120     update : function(params, records){
12121         
12122     }
12123 });/*
12124  * Based on:
12125  * Ext JS Library 1.1.1
12126  * Copyright(c) 2006-2007, Ext JS, LLC.
12127  *
12128  * Originally Released Under LGPL - original licence link has changed is not relivant.
12129  *
12130  * Fork - LGPL
12131  * <script type="text/javascript">
12132  */
12133 /**
12134  * @class Roo.data.HttpProxy
12135  * @extends Roo.data.DataProxy
12136  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12137  * configured to reference a certain URL.<br><br>
12138  * <p>
12139  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12140  * from which the running page was served.<br><br>
12141  * <p>
12142  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12143  * <p>
12144  * Be aware that to enable the browser to parse an XML document, the server must set
12145  * the Content-Type header in the HTTP response to "text/xml".
12146  * @constructor
12147  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12148  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12149  * will be used to make the request.
12150  */
12151 Roo.data.HttpProxy = function(conn){
12152     Roo.data.HttpProxy.superclass.constructor.call(this);
12153     // is conn a conn config or a real conn?
12154     this.conn = conn;
12155     this.useAjax = !conn || !conn.events;
12156   
12157 };
12158
12159 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12160     // thse are take from connection...
12161     
12162     /**
12163      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12164      */
12165     /**
12166      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12167      * extra parameters to each request made by this object. (defaults to undefined)
12168      */
12169     /**
12170      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12171      *  to each request made by this object. (defaults to undefined)
12172      */
12173     /**
12174      * @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)
12175      */
12176     /**
12177      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12178      */
12179      /**
12180      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12181      * @type Boolean
12182      */
12183   
12184
12185     /**
12186      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12187      * @type Boolean
12188      */
12189     /**
12190      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12191      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12192      * a finer-grained basis than the DataProxy events.
12193      */
12194     getConnection : function(){
12195         return this.useAjax ? Roo.Ajax : this.conn;
12196     },
12197
12198     /**
12199      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12200      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12201      * process that block using the passed callback.
12202      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12203      * for the request to the remote server.
12204      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12205      * object into a block of Roo.data.Records.
12206      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12207      * The function must be passed <ul>
12208      * <li>The Record block object</li>
12209      * <li>The "arg" argument from the load function</li>
12210      * <li>A boolean success indicator</li>
12211      * </ul>
12212      * @param {Object} scope The scope in which to call the callback
12213      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12214      */
12215     load : function(params, reader, callback, scope, arg){
12216         if(this.fireEvent("beforeload", this, params) !== false){
12217             var  o = {
12218                 params : params || {},
12219                 request: {
12220                     callback : callback,
12221                     scope : scope,
12222                     arg : arg
12223                 },
12224                 reader: reader,
12225                 callback : this.loadResponse,
12226                 scope: this
12227             };
12228             if(this.useAjax){
12229                 Roo.applyIf(o, this.conn);
12230                 if(this.activeRequest){
12231                     Roo.Ajax.abort(this.activeRequest);
12232                 }
12233                 this.activeRequest = Roo.Ajax.request(o);
12234             }else{
12235                 this.conn.request(o);
12236             }
12237         }else{
12238             callback.call(scope||this, null, arg, false);
12239         }
12240     },
12241
12242     // private
12243     loadResponse : function(o, success, response){
12244         delete this.activeRequest;
12245         if(!success){
12246             this.fireEvent("loadexception", this, o, response);
12247             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12248             return;
12249         }
12250         var result;
12251         try {
12252             result = o.reader.read(response);
12253         }catch(e){
12254             this.fireEvent("loadexception", this, o, response, e);
12255             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12256             return;
12257         }
12258         
12259         this.fireEvent("load", this, o, o.request.arg);
12260         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12261     },
12262
12263     // private
12264     update : function(dataSet){
12265
12266     },
12267
12268     // private
12269     updateResponse : function(dataSet){
12270
12271     }
12272 });/*
12273  * Based on:
12274  * Ext JS Library 1.1.1
12275  * Copyright(c) 2006-2007, Ext JS, LLC.
12276  *
12277  * Originally Released Under LGPL - original licence link has changed is not relivant.
12278  *
12279  * Fork - LGPL
12280  * <script type="text/javascript">
12281  */
12282
12283 /**
12284  * @class Roo.data.ScriptTagProxy
12285  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12286  * other than the originating domain of the running page.<br><br>
12287  * <p>
12288  * <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
12289  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12290  * <p>
12291  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12292  * source code that is used as the source inside a &lt;script> tag.<br><br>
12293  * <p>
12294  * In order for the browser to process the returned data, the server must wrap the data object
12295  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12296  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12297  * depending on whether the callback name was passed:
12298  * <p>
12299  * <pre><code>
12300 boolean scriptTag = false;
12301 String cb = request.getParameter("callback");
12302 if (cb != null) {
12303     scriptTag = true;
12304     response.setContentType("text/javascript");
12305 } else {
12306     response.setContentType("application/x-json");
12307 }
12308 Writer out = response.getWriter();
12309 if (scriptTag) {
12310     out.write(cb + "(");
12311 }
12312 out.print(dataBlock.toJsonString());
12313 if (scriptTag) {
12314     out.write(");");
12315 }
12316 </pre></code>
12317  *
12318  * @constructor
12319  * @param {Object} config A configuration object.
12320  */
12321 Roo.data.ScriptTagProxy = function(config){
12322     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12323     Roo.apply(this, config);
12324     this.head = document.getElementsByTagName("head")[0];
12325 };
12326
12327 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12328
12329 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12330     /**
12331      * @cfg {String} url The URL from which to request the data object.
12332      */
12333     /**
12334      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12335      */
12336     timeout : 30000,
12337     /**
12338      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12339      * the server the name of the callback function set up by the load call to process the returned data object.
12340      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12341      * javascript output which calls this named function passing the data object as its only parameter.
12342      */
12343     callbackParam : "callback",
12344     /**
12345      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12346      * name to the request.
12347      */
12348     nocache : true,
12349
12350     /**
12351      * Load data from the configured URL, read the data object into
12352      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12353      * process that block using the passed callback.
12354      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12355      * for the request to the remote server.
12356      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12357      * object into a block of Roo.data.Records.
12358      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12359      * The function must be passed <ul>
12360      * <li>The Record block object</li>
12361      * <li>The "arg" argument from the load function</li>
12362      * <li>A boolean success indicator</li>
12363      * </ul>
12364      * @param {Object} scope The scope in which to call the callback
12365      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12366      */
12367     load : function(params, reader, callback, scope, arg){
12368         if(this.fireEvent("beforeload", this, params) !== false){
12369
12370             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12371
12372             var url = this.url;
12373             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12374             if(this.nocache){
12375                 url += "&_dc=" + (new Date().getTime());
12376             }
12377             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12378             var trans = {
12379                 id : transId,
12380                 cb : "stcCallback"+transId,
12381                 scriptId : "stcScript"+transId,
12382                 params : params,
12383                 arg : arg,
12384                 url : url,
12385                 callback : callback,
12386                 scope : scope,
12387                 reader : reader
12388             };
12389             var conn = this;
12390
12391             window[trans.cb] = function(o){
12392                 conn.handleResponse(o, trans);
12393             };
12394
12395             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12396
12397             if(this.autoAbort !== false){
12398                 this.abort();
12399             }
12400
12401             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12402
12403             var script = document.createElement("script");
12404             script.setAttribute("src", url);
12405             script.setAttribute("type", "text/javascript");
12406             script.setAttribute("id", trans.scriptId);
12407             this.head.appendChild(script);
12408
12409             this.trans = trans;
12410         }else{
12411             callback.call(scope||this, null, arg, false);
12412         }
12413     },
12414
12415     // private
12416     isLoading : function(){
12417         return this.trans ? true : false;
12418     },
12419
12420     /**
12421      * Abort the current server request.
12422      */
12423     abort : function(){
12424         if(this.isLoading()){
12425             this.destroyTrans(this.trans);
12426         }
12427     },
12428
12429     // private
12430     destroyTrans : function(trans, isLoaded){
12431         this.head.removeChild(document.getElementById(trans.scriptId));
12432         clearTimeout(trans.timeoutId);
12433         if(isLoaded){
12434             window[trans.cb] = undefined;
12435             try{
12436                 delete window[trans.cb];
12437             }catch(e){}
12438         }else{
12439             // if hasn't been loaded, wait for load to remove it to prevent script error
12440             window[trans.cb] = function(){
12441                 window[trans.cb] = undefined;
12442                 try{
12443                     delete window[trans.cb];
12444                 }catch(e){}
12445             };
12446         }
12447     },
12448
12449     // private
12450     handleResponse : function(o, trans){
12451         this.trans = false;
12452         this.destroyTrans(trans, true);
12453         var result;
12454         try {
12455             result = trans.reader.readRecords(o);
12456         }catch(e){
12457             this.fireEvent("loadexception", this, o, trans.arg, e);
12458             trans.callback.call(trans.scope||window, null, trans.arg, false);
12459             return;
12460         }
12461         this.fireEvent("load", this, o, trans.arg);
12462         trans.callback.call(trans.scope||window, result, trans.arg, true);
12463     },
12464
12465     // private
12466     handleFailure : function(trans){
12467         this.trans = false;
12468         this.destroyTrans(trans, false);
12469         this.fireEvent("loadexception", this, null, trans.arg);
12470         trans.callback.call(trans.scope||window, null, trans.arg, false);
12471     }
12472 });/*
12473  * Based on:
12474  * Ext JS Library 1.1.1
12475  * Copyright(c) 2006-2007, Ext JS, LLC.
12476  *
12477  * Originally Released Under LGPL - original licence link has changed is not relivant.
12478  *
12479  * Fork - LGPL
12480  * <script type="text/javascript">
12481  */
12482
12483 /**
12484  * @class Roo.data.JsonReader
12485  * @extends Roo.data.DataReader
12486  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12487  * based on mappings in a provided Roo.data.Record constructor.
12488  * 
12489  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12490  * in the reply previously. 
12491  * 
12492  * <p>
12493  * Example code:
12494  * <pre><code>
12495 var RecordDef = Roo.data.Record.create([
12496     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12497     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12498 ]);
12499 var myReader = new Roo.data.JsonReader({
12500     totalProperty: "results",    // The property which contains the total dataset size (optional)
12501     root: "rows",                // The property which contains an Array of row objects
12502     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12503 }, RecordDef);
12504 </code></pre>
12505  * <p>
12506  * This would consume a JSON file like this:
12507  * <pre><code>
12508 { 'results': 2, 'rows': [
12509     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12510     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12511 }
12512 </code></pre>
12513  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12514  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12515  * paged from the remote server.
12516  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12517  * @cfg {String} root name of the property which contains the Array of row objects.
12518  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12519  * @cfg {Array} fields Array of field definition objects
12520  * @constructor
12521  * Create a new JsonReader
12522  * @param {Object} meta Metadata configuration options
12523  * @param {Object} recordType Either an Array of field definition objects,
12524  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12525  */
12526 Roo.data.JsonReader = function(meta, recordType){
12527     
12528     meta = meta || {};
12529     // set some defaults:
12530     Roo.applyIf(meta, {
12531         totalProperty: 'total',
12532         successProperty : 'success',
12533         root : 'data',
12534         id : 'id'
12535     });
12536     
12537     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12538 };
12539 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12540     
12541     /**
12542      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12543      * Used by Store query builder to append _requestMeta to params.
12544      * 
12545      */
12546     metaFromRemote : false,
12547     /**
12548      * This method is only used by a DataProxy which has retrieved data from a remote server.
12549      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12550      * @return {Object} data A data block which is used by an Roo.data.Store object as
12551      * a cache of Roo.data.Records.
12552      */
12553     read : function(response){
12554         var json = response.responseText;
12555        
12556         var o = /* eval:var:o */ eval("("+json+")");
12557         if(!o) {
12558             throw {message: "JsonReader.read: Json object not found"};
12559         }
12560         
12561         if(o.metaData){
12562             
12563             delete this.ef;
12564             this.metaFromRemote = true;
12565             this.meta = o.metaData;
12566             this.recordType = Roo.data.Record.create(o.metaData.fields);
12567             this.onMetaChange(this.meta, this.recordType, o);
12568         }
12569         return this.readRecords(o);
12570     },
12571
12572     // private function a store will implement
12573     onMetaChange : function(meta, recordType, o){
12574
12575     },
12576
12577     /**
12578          * @ignore
12579          */
12580     simpleAccess: function(obj, subsc) {
12581         return obj[subsc];
12582     },
12583
12584         /**
12585          * @ignore
12586          */
12587     getJsonAccessor: function(){
12588         var re = /[\[\.]/;
12589         return function(expr) {
12590             try {
12591                 return(re.test(expr))
12592                     ? new Function("obj", "return obj." + expr)
12593                     : function(obj){
12594                         return obj[expr];
12595                     };
12596             } catch(e){}
12597             return Roo.emptyFn;
12598         };
12599     }(),
12600
12601     /**
12602      * Create a data block containing Roo.data.Records from an XML document.
12603      * @param {Object} o An object which contains an Array of row objects in the property specified
12604      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12605      * which contains the total size of the dataset.
12606      * @return {Object} data A data block which is used by an Roo.data.Store object as
12607      * a cache of Roo.data.Records.
12608      */
12609     readRecords : function(o){
12610         /**
12611          * After any data loads, the raw JSON data is available for further custom processing.
12612          * @type Object
12613          */
12614         this.o = o;
12615         var s = this.meta, Record = this.recordType,
12616             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12617
12618 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12619         if (!this.ef) {
12620             if(s.totalProperty) {
12621                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12622                 }
12623                 if(s.successProperty) {
12624                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12625                 }
12626                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12627                 if (s.id) {
12628                         var g = this.getJsonAccessor(s.id);
12629                         this.getId = function(rec) {
12630                                 var r = g(rec);  
12631                                 return (r === undefined || r === "") ? null : r;
12632                         };
12633                 } else {
12634                         this.getId = function(){return null;};
12635                 }
12636             this.ef = [];
12637             for(var jj = 0; jj < fl; jj++){
12638                 f = fi[jj];
12639                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12640                 this.ef[jj] = this.getJsonAccessor(map);
12641             }
12642         }
12643
12644         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12645         if(s.totalProperty){
12646             var vt = parseInt(this.getTotal(o), 10);
12647             if(!isNaN(vt)){
12648                 totalRecords = vt;
12649             }
12650         }
12651         if(s.successProperty){
12652             var vs = this.getSuccess(o);
12653             if(vs === false || vs === 'false'){
12654                 success = false;
12655             }
12656         }
12657         var records = [];
12658         for(var i = 0; i < c; i++){
12659                 var n = root[i];
12660             var values = {};
12661             var id = this.getId(n);
12662             for(var j = 0; j < fl; j++){
12663                 f = fi[j];
12664             var v = this.ef[j](n);
12665             if (!f.convert) {
12666                 Roo.log('missing convert for ' + f.name);
12667                 Roo.log(f);
12668                 continue;
12669             }
12670             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12671             }
12672             var record = new Record(values, id);
12673             record.json = n;
12674             records[i] = record;
12675         }
12676         return {
12677             raw : o,
12678             success : success,
12679             records : records,
12680             totalRecords : totalRecords
12681         };
12682     }
12683 });/*
12684  * Based on:
12685  * Ext JS Library 1.1.1
12686  * Copyright(c) 2006-2007, Ext JS, LLC.
12687  *
12688  * Originally Released Under LGPL - original licence link has changed is not relivant.
12689  *
12690  * Fork - LGPL
12691  * <script type="text/javascript">
12692  */
12693
12694 /**
12695  * @class Roo.data.ArrayReader
12696  * @extends Roo.data.DataReader
12697  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12698  * Each element of that Array represents a row of data fields. The
12699  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12700  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12701  * <p>
12702  * Example code:.
12703  * <pre><code>
12704 var RecordDef = Roo.data.Record.create([
12705     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12706     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12707 ]);
12708 var myReader = new Roo.data.ArrayReader({
12709     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12710 }, RecordDef);
12711 </code></pre>
12712  * <p>
12713  * This would consume an Array like this:
12714  * <pre><code>
12715 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12716   </code></pre>
12717  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12718  * @constructor
12719  * Create a new JsonReader
12720  * @param {Object} meta Metadata configuration options.
12721  * @param {Object} recordType Either an Array of field definition objects
12722  * as specified to {@link Roo.data.Record#create},
12723  * or an {@link Roo.data.Record} object
12724  * created using {@link Roo.data.Record#create}.
12725  */
12726 Roo.data.ArrayReader = function(meta, recordType){
12727     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12728 };
12729
12730 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12731     /**
12732      * Create a data block containing Roo.data.Records from an XML document.
12733      * @param {Object} o An Array of row objects which represents the dataset.
12734      * @return {Object} data A data block which is used by an Roo.data.Store object as
12735      * a cache of Roo.data.Records.
12736      */
12737     readRecords : function(o){
12738         var sid = this.meta ? this.meta.id : null;
12739         var recordType = this.recordType, fields = recordType.prototype.fields;
12740         var records = [];
12741         var root = o;
12742             for(var i = 0; i < root.length; i++){
12743                     var n = root[i];
12744                 var values = {};
12745                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12746                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12747                 var f = fields.items[j];
12748                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12749                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12750                 v = f.convert(v);
12751                 values[f.name] = v;
12752             }
12753                 var record = new recordType(values, id);
12754                 record.json = n;
12755                 records[records.length] = record;
12756             }
12757             return {
12758                 records : records,
12759                 totalRecords : records.length
12760             };
12761     }
12762 });/*
12763  * - LGPL
12764  * * 
12765  */
12766
12767 /**
12768  * @class Roo.bootstrap.ComboBox
12769  * @extends Roo.bootstrap.TriggerField
12770  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12771  * @cfg {Boolean} append (true|false) default false
12772  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12773  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12774  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12775  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12776  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12777  * @cfg {Boolean} animate default true
12778  * @cfg {Boolean} emptyResultText only for touch device
12779  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12780  * @cfg {String} emptyTitle default ''
12781  * @constructor
12782  * Create a new ComboBox.
12783  * @param {Object} config Configuration options
12784  */
12785 Roo.bootstrap.ComboBox = function(config){
12786     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12787     this.addEvents({
12788         /**
12789          * @event expand
12790          * Fires when the dropdown list is expanded
12791         * @param {Roo.bootstrap.ComboBox} combo This combo box
12792         */
12793         'expand' : true,
12794         /**
12795          * @event collapse
12796          * Fires when the dropdown list is collapsed
12797         * @param {Roo.bootstrap.ComboBox} combo This combo box
12798         */
12799         'collapse' : true,
12800         /**
12801          * @event beforeselect
12802          * Fires before a list item is selected. Return false to cancel the selection.
12803         * @param {Roo.bootstrap.ComboBox} combo This combo box
12804         * @param {Roo.data.Record} record The data record returned from the underlying store
12805         * @param {Number} index The index of the selected item in the dropdown list
12806         */
12807         'beforeselect' : true,
12808         /**
12809          * @event select
12810          * Fires when a list item is selected
12811         * @param {Roo.bootstrap.ComboBox} combo This combo box
12812         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12813         * @param {Number} index The index of the selected item in the dropdown list
12814         */
12815         'select' : true,
12816         /**
12817          * @event beforequery
12818          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12819          * The event object passed has these properties:
12820         * @param {Roo.bootstrap.ComboBox} combo This combo box
12821         * @param {String} query The query
12822         * @param {Boolean} forceAll true to force "all" query
12823         * @param {Boolean} cancel true to cancel the query
12824         * @param {Object} e The query event object
12825         */
12826         'beforequery': true,
12827          /**
12828          * @event add
12829          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12830         * @param {Roo.bootstrap.ComboBox} combo This combo box
12831         */
12832         'add' : true,
12833         /**
12834          * @event edit
12835          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12836         * @param {Roo.bootstrap.ComboBox} combo This combo box
12837         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12838         */
12839         'edit' : true,
12840         /**
12841          * @event remove
12842          * Fires when the remove value from the combobox array
12843         * @param {Roo.bootstrap.ComboBox} combo This combo box
12844         */
12845         'remove' : true,
12846         /**
12847          * @event afterremove
12848          * Fires when the remove value from the combobox array
12849         * @param {Roo.bootstrap.ComboBox} combo This combo box
12850         */
12851         'afterremove' : true,
12852         /**
12853          * @event specialfilter
12854          * Fires when specialfilter
12855             * @param {Roo.bootstrap.ComboBox} combo This combo box
12856             */
12857         'specialfilter' : true,
12858         /**
12859          * @event tick
12860          * Fires when tick the element
12861             * @param {Roo.bootstrap.ComboBox} combo This combo box
12862             */
12863         'tick' : true,
12864         /**
12865          * @event touchviewdisplay
12866          * Fires when touch view require special display (default is using displayField)
12867             * @param {Roo.bootstrap.ComboBox} combo This combo box
12868             * @param {Object} cfg set html .
12869             */
12870         'touchviewdisplay' : true
12871         
12872     });
12873     
12874     this.item = [];
12875     this.tickItems = [];
12876     
12877     this.selectedIndex = -1;
12878     if(this.mode == 'local'){
12879         if(config.queryDelay === undefined){
12880             this.queryDelay = 10;
12881         }
12882         if(config.minChars === undefined){
12883             this.minChars = 0;
12884         }
12885     }
12886 };
12887
12888 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12889      
12890     /**
12891      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12892      * rendering into an Roo.Editor, defaults to false)
12893      */
12894     /**
12895      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12896      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12897      */
12898     /**
12899      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12900      */
12901     /**
12902      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12903      * the dropdown list (defaults to undefined, with no header element)
12904      */
12905
12906      /**
12907      * @cfg {String/Roo.Template} tpl The template to use to render the output
12908      */
12909      
12910      /**
12911      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12912      */
12913     listWidth: undefined,
12914     /**
12915      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12916      * mode = 'remote' or 'text' if mode = 'local')
12917      */
12918     displayField: undefined,
12919     
12920     /**
12921      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12922      * mode = 'remote' or 'value' if mode = 'local'). 
12923      * Note: use of a valueField requires the user make a selection
12924      * in order for a value to be mapped.
12925      */
12926     valueField: undefined,
12927     /**
12928      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12929      */
12930     modalTitle : '',
12931     
12932     /**
12933      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12934      * field's data value (defaults to the underlying DOM element's name)
12935      */
12936     hiddenName: undefined,
12937     /**
12938      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12939      */
12940     listClass: '',
12941     /**
12942      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12943      */
12944     selectedClass: 'active',
12945     
12946     /**
12947      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12948      */
12949     shadow:'sides',
12950     /**
12951      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12952      * anchor positions (defaults to 'tl-bl')
12953      */
12954     listAlign: 'tl-bl?',
12955     /**
12956      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12957      */
12958     maxHeight: 300,
12959     /**
12960      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12961      * query specified by the allQuery config option (defaults to 'query')
12962      */
12963     triggerAction: 'query',
12964     /**
12965      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12966      * (defaults to 4, does not apply if editable = false)
12967      */
12968     minChars : 4,
12969     /**
12970      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12971      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12972      */
12973     typeAhead: false,
12974     /**
12975      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12976      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12977      */
12978     queryDelay: 500,
12979     /**
12980      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12981      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12982      */
12983     pageSize: 0,
12984     /**
12985      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12986      * when editable = true (defaults to false)
12987      */
12988     selectOnFocus:false,
12989     /**
12990      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12991      */
12992     queryParam: 'query',
12993     /**
12994      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12995      * when mode = 'remote' (defaults to 'Loading...')
12996      */
12997     loadingText: 'Loading...',
12998     /**
12999      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13000      */
13001     resizable: false,
13002     /**
13003      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13004      */
13005     handleHeight : 8,
13006     /**
13007      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13008      * traditional select (defaults to true)
13009      */
13010     editable: true,
13011     /**
13012      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13013      */
13014     allQuery: '',
13015     /**
13016      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13017      */
13018     mode: 'remote',
13019     /**
13020      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13021      * listWidth has a higher value)
13022      */
13023     minListWidth : 70,
13024     /**
13025      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13026      * allow the user to set arbitrary text into the field (defaults to false)
13027      */
13028     forceSelection:false,
13029     /**
13030      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13031      * if typeAhead = true (defaults to 250)
13032      */
13033     typeAheadDelay : 250,
13034     /**
13035      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13036      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13037      */
13038     valueNotFoundText : undefined,
13039     /**
13040      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13041      */
13042     blockFocus : false,
13043     
13044     /**
13045      * @cfg {Boolean} disableClear Disable showing of clear button.
13046      */
13047     disableClear : false,
13048     /**
13049      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13050      */
13051     alwaysQuery : false,
13052     
13053     /**
13054      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13055      */
13056     multiple : false,
13057     
13058     /**
13059      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13060      */
13061     invalidClass : "has-warning",
13062     
13063     /**
13064      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13065      */
13066     validClass : "has-success",
13067     
13068     /**
13069      * @cfg {Boolean} specialFilter (true|false) special filter default false
13070      */
13071     specialFilter : false,
13072     
13073     /**
13074      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13075      */
13076     mobileTouchView : true,
13077     
13078     /**
13079      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13080      */
13081     useNativeIOS : false,
13082     
13083     /**
13084      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13085      */
13086     mobile_restrict_height : false,
13087     
13088     ios_options : false,
13089     
13090     //private
13091     addicon : false,
13092     editicon: false,
13093     
13094     page: 0,
13095     hasQuery: false,
13096     append: false,
13097     loadNext: false,
13098     autoFocus : true,
13099     tickable : false,
13100     btnPosition : 'right',
13101     triggerList : true,
13102     showToggleBtn : true,
13103     animate : true,
13104     emptyResultText: 'Empty',
13105     triggerText : 'Select',
13106     emptyTitle : '',
13107     
13108     // element that contains real text value.. (when hidden is used..)
13109     
13110     getAutoCreate : function()
13111     {   
13112         var cfg = false;
13113         //render
13114         /*
13115          * Render classic select for iso
13116          */
13117         
13118         if(Roo.isIOS && this.useNativeIOS){
13119             cfg = this.getAutoCreateNativeIOS();
13120             return cfg;
13121         }
13122         
13123         /*
13124          * Touch Devices
13125          */
13126         
13127         if(Roo.isTouch && this.mobileTouchView){
13128             cfg = this.getAutoCreateTouchView();
13129             return cfg;;
13130         }
13131         
13132         /*
13133          *  Normal ComboBox
13134          */
13135         if(!this.tickable){
13136             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13137             return cfg;
13138         }
13139         
13140         /*
13141          *  ComboBox with tickable selections
13142          */
13143              
13144         var align = this.labelAlign || this.parentLabelAlign();
13145         
13146         cfg = {
13147             cls : 'form-group roo-combobox-tickable' //input-group
13148         };
13149         
13150         var btn_text_select = '';
13151         var btn_text_done = '';
13152         var btn_text_cancel = '';
13153         
13154         if (this.btn_text_show) {
13155             btn_text_select = 'Select';
13156             btn_text_done = 'Done';
13157             btn_text_cancel = 'Cancel'; 
13158         }
13159         
13160         var buttons = {
13161             tag : 'div',
13162             cls : 'tickable-buttons',
13163             cn : [
13164                 {
13165                     tag : 'button',
13166                     type : 'button',
13167                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13168                     //html : this.triggerText
13169                     html: btn_text_select
13170                 },
13171                 {
13172                     tag : 'button',
13173                     type : 'button',
13174                     name : 'ok',
13175                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13176                     //html : 'Done'
13177                     html: btn_text_done
13178                 },
13179                 {
13180                     tag : 'button',
13181                     type : 'button',
13182                     name : 'cancel',
13183                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13184                     //html : 'Cancel'
13185                     html: btn_text_cancel
13186                 }
13187             ]
13188         };
13189         
13190         if(this.editable){
13191             buttons.cn.unshift({
13192                 tag: 'input',
13193                 cls: 'roo-select2-search-field-input'
13194             });
13195         }
13196         
13197         var _this = this;
13198         
13199         Roo.each(buttons.cn, function(c){
13200             if (_this.size) {
13201                 c.cls += ' btn-' + _this.size;
13202             }
13203
13204             if (_this.disabled) {
13205                 c.disabled = true;
13206             }
13207         });
13208         
13209         var box = {
13210             tag: 'div',
13211             cn: [
13212                 {
13213                     tag: 'input',
13214                     type : 'hidden',
13215                     cls: 'form-hidden-field'
13216                 },
13217                 {
13218                     tag: 'ul',
13219                     cls: 'roo-select2-choices',
13220                     cn:[
13221                         {
13222                             tag: 'li',
13223                             cls: 'roo-select2-search-field',
13224                             cn: [
13225                                 buttons
13226                             ]
13227                         }
13228                     ]
13229                 }
13230             ]
13231         };
13232         
13233         var combobox = {
13234             cls: 'roo-select2-container input-group roo-select2-container-multi',
13235             cn: [
13236                 box
13237 //                {
13238 //                    tag: 'ul',
13239 //                    cls: 'typeahead typeahead-long dropdown-menu',
13240 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13241 //                }
13242             ]
13243         };
13244         
13245         if(this.hasFeedback && !this.allowBlank){
13246             
13247             var feedback = {
13248                 tag: 'span',
13249                 cls: 'glyphicon form-control-feedback'
13250             };
13251
13252             combobox.cn.push(feedback);
13253         }
13254         
13255         
13256         if (align ==='left' && this.fieldLabel.length) {
13257             
13258             cfg.cls += ' roo-form-group-label-left';
13259             
13260             cfg.cn = [
13261                 {
13262                     tag : 'i',
13263                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13264                     tooltip : 'This field is required'
13265                 },
13266                 {
13267                     tag: 'label',
13268                     'for' :  id,
13269                     cls : 'control-label',
13270                     html : this.fieldLabel
13271
13272                 },
13273                 {
13274                     cls : "", 
13275                     cn: [
13276                         combobox
13277                     ]
13278                 }
13279
13280             ];
13281             
13282             var labelCfg = cfg.cn[1];
13283             var contentCfg = cfg.cn[2];
13284             
13285
13286             if(this.indicatorpos == 'right'){
13287                 
13288                 cfg.cn = [
13289                     {
13290                         tag: 'label',
13291                         'for' :  id,
13292                         cls : 'control-label',
13293                         cn : [
13294                             {
13295                                 tag : 'span',
13296                                 html : this.fieldLabel
13297                             },
13298                             {
13299                                 tag : 'i',
13300                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13301                                 tooltip : 'This field is required'
13302                             }
13303                         ]
13304                     },
13305                     {
13306                         cls : "",
13307                         cn: [
13308                             combobox
13309                         ]
13310                     }
13311
13312                 ];
13313                 
13314                 
13315                 
13316                 labelCfg = cfg.cn[0];
13317                 contentCfg = cfg.cn[1];
13318             
13319             }
13320             
13321             if(this.labelWidth > 12){
13322                 labelCfg.style = "width: " + this.labelWidth + 'px';
13323             }
13324             
13325             if(this.labelWidth < 13 && this.labelmd == 0){
13326                 this.labelmd = this.labelWidth;
13327             }
13328             
13329             if(this.labellg > 0){
13330                 labelCfg.cls += ' col-lg-' + this.labellg;
13331                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13332             }
13333             
13334             if(this.labelmd > 0){
13335                 labelCfg.cls += ' col-md-' + this.labelmd;
13336                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13337             }
13338             
13339             if(this.labelsm > 0){
13340                 labelCfg.cls += ' col-sm-' + this.labelsm;
13341                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13342             }
13343             
13344             if(this.labelxs > 0){
13345                 labelCfg.cls += ' col-xs-' + this.labelxs;
13346                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13347             }
13348                 
13349                 
13350         } else if ( this.fieldLabel.length) {
13351 //                Roo.log(" label");
13352                  cfg.cn = [
13353                     {
13354                         tag : 'i',
13355                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13356                         tooltip : 'This field is required'
13357                     },
13358                     {
13359                         tag: 'label',
13360                         //cls : 'input-group-addon',
13361                         html : this.fieldLabel
13362                     },
13363                     combobox
13364                 ];
13365                 
13366                 if(this.indicatorpos == 'right'){
13367                     cfg.cn = [
13368                         {
13369                             tag: 'label',
13370                             //cls : 'input-group-addon',
13371                             html : this.fieldLabel
13372                         },
13373                         {
13374                             tag : 'i',
13375                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13376                             tooltip : 'This field is required'
13377                         },
13378                         combobox
13379                     ];
13380                     
13381                 }
13382
13383         } else {
13384             
13385 //                Roo.log(" no label && no align");
13386                 cfg = combobox
13387                      
13388                 
13389         }
13390          
13391         var settings=this;
13392         ['xs','sm','md','lg'].map(function(size){
13393             if (settings[size]) {
13394                 cfg.cls += ' col-' + size + '-' + settings[size];
13395             }
13396         });
13397         
13398         return cfg;
13399         
13400     },
13401     
13402     _initEventsCalled : false,
13403     
13404     // private
13405     initEvents: function()
13406     {   
13407         if (this._initEventsCalled) { // as we call render... prevent looping...
13408             return;
13409         }
13410         this._initEventsCalled = true;
13411         
13412         if (!this.store) {
13413             throw "can not find store for combo";
13414         }
13415         
13416         this.indicator = this.indicatorEl();
13417         
13418         this.store = Roo.factory(this.store, Roo.data);
13419         this.store.parent = this;
13420         
13421         // if we are building from html. then this element is so complex, that we can not really
13422         // use the rendered HTML.
13423         // so we have to trash and replace the previous code.
13424         if (Roo.XComponent.build_from_html) {
13425             // remove this element....
13426             var e = this.el.dom, k=0;
13427             while (e ) { e = e.previousSibling;  ++k;}
13428
13429             this.el.remove();
13430             
13431             this.el=false;
13432             this.rendered = false;
13433             
13434             this.render(this.parent().getChildContainer(true), k);
13435         }
13436         
13437         if(Roo.isIOS && this.useNativeIOS){
13438             this.initIOSView();
13439             return;
13440         }
13441         
13442         /*
13443          * Touch Devices
13444          */
13445         
13446         if(Roo.isTouch && this.mobileTouchView){
13447             this.initTouchView();
13448             return;
13449         }
13450         
13451         if(this.tickable){
13452             this.initTickableEvents();
13453             return;
13454         }
13455         
13456         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13457         
13458         if(this.hiddenName){
13459             
13460             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13461             
13462             this.hiddenField.dom.value =
13463                 this.hiddenValue !== undefined ? this.hiddenValue :
13464                 this.value !== undefined ? this.value : '';
13465
13466             // prevent input submission
13467             this.el.dom.removeAttribute('name');
13468             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13469              
13470              
13471         }
13472         //if(Roo.isGecko){
13473         //    this.el.dom.setAttribute('autocomplete', 'off');
13474         //}
13475         
13476         var cls = 'x-combo-list';
13477         
13478         //this.list = new Roo.Layer({
13479         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13480         //});
13481         
13482         var _this = this;
13483         
13484         (function(){
13485             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13486             _this.list.setWidth(lw);
13487         }).defer(100);
13488         
13489         this.list.on('mouseover', this.onViewOver, this);
13490         this.list.on('mousemove', this.onViewMove, this);
13491         this.list.on('scroll', this.onViewScroll, this);
13492         
13493         /*
13494         this.list.swallowEvent('mousewheel');
13495         this.assetHeight = 0;
13496
13497         if(this.title){
13498             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13499             this.assetHeight += this.header.getHeight();
13500         }
13501
13502         this.innerList = this.list.createChild({cls:cls+'-inner'});
13503         this.innerList.on('mouseover', this.onViewOver, this);
13504         this.innerList.on('mousemove', this.onViewMove, this);
13505         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13506         
13507         if(this.allowBlank && !this.pageSize && !this.disableClear){
13508             this.footer = this.list.createChild({cls:cls+'-ft'});
13509             this.pageTb = new Roo.Toolbar(this.footer);
13510            
13511         }
13512         if(this.pageSize){
13513             this.footer = this.list.createChild({cls:cls+'-ft'});
13514             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13515                     {pageSize: this.pageSize});
13516             
13517         }
13518         
13519         if (this.pageTb && this.allowBlank && !this.disableClear) {
13520             var _this = this;
13521             this.pageTb.add(new Roo.Toolbar.Fill(), {
13522                 cls: 'x-btn-icon x-btn-clear',
13523                 text: '&#160;',
13524                 handler: function()
13525                 {
13526                     _this.collapse();
13527                     _this.clearValue();
13528                     _this.onSelect(false, -1);
13529                 }
13530             });
13531         }
13532         if (this.footer) {
13533             this.assetHeight += this.footer.getHeight();
13534         }
13535         */
13536             
13537         if(!this.tpl){
13538             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13539         }
13540
13541         this.view = new Roo.View(this.list, this.tpl, {
13542             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13543         });
13544         //this.view.wrapEl.setDisplayed(false);
13545         this.view.on('click', this.onViewClick, this);
13546         
13547         
13548         this.store.on('beforeload', this.onBeforeLoad, this);
13549         this.store.on('load', this.onLoad, this);
13550         this.store.on('loadexception', this.onLoadException, this);
13551         /*
13552         if(this.resizable){
13553             this.resizer = new Roo.Resizable(this.list,  {
13554                pinned:true, handles:'se'
13555             });
13556             this.resizer.on('resize', function(r, w, h){
13557                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13558                 this.listWidth = w;
13559                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13560                 this.restrictHeight();
13561             }, this);
13562             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13563         }
13564         */
13565         if(!this.editable){
13566             this.editable = true;
13567             this.setEditable(false);
13568         }
13569         
13570         /*
13571         
13572         if (typeof(this.events.add.listeners) != 'undefined') {
13573             
13574             this.addicon = this.wrap.createChild(
13575                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13576        
13577             this.addicon.on('click', function(e) {
13578                 this.fireEvent('add', this);
13579             }, this);
13580         }
13581         if (typeof(this.events.edit.listeners) != 'undefined') {
13582             
13583             this.editicon = this.wrap.createChild(
13584                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13585             if (this.addicon) {
13586                 this.editicon.setStyle('margin-left', '40px');
13587             }
13588             this.editicon.on('click', function(e) {
13589                 
13590                 // we fire even  if inothing is selected..
13591                 this.fireEvent('edit', this, this.lastData );
13592                 
13593             }, this);
13594         }
13595         */
13596         
13597         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13598             "up" : function(e){
13599                 this.inKeyMode = true;
13600                 this.selectPrev();
13601             },
13602
13603             "down" : function(e){
13604                 if(!this.isExpanded()){
13605                     this.onTriggerClick();
13606                 }else{
13607                     this.inKeyMode = true;
13608                     this.selectNext();
13609                 }
13610             },
13611
13612             "enter" : function(e){
13613 //                this.onViewClick();
13614                 //return true;
13615                 this.collapse();
13616                 
13617                 if(this.fireEvent("specialkey", this, e)){
13618                     this.onViewClick(false);
13619                 }
13620                 
13621                 return true;
13622             },
13623
13624             "esc" : function(e){
13625                 this.collapse();
13626             },
13627
13628             "tab" : function(e){
13629                 this.collapse();
13630                 
13631                 if(this.fireEvent("specialkey", this, e)){
13632                     this.onViewClick(false);
13633                 }
13634                 
13635                 return true;
13636             },
13637
13638             scope : this,
13639
13640             doRelay : function(foo, bar, hname){
13641                 if(hname == 'down' || this.scope.isExpanded()){
13642                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13643                 }
13644                 return true;
13645             },
13646
13647             forceKeyDown: true
13648         });
13649         
13650         
13651         this.queryDelay = Math.max(this.queryDelay || 10,
13652                 this.mode == 'local' ? 10 : 250);
13653         
13654         
13655         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13656         
13657         if(this.typeAhead){
13658             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13659         }
13660         if(this.editable !== false){
13661             this.inputEl().on("keyup", this.onKeyUp, this);
13662         }
13663         if(this.forceSelection){
13664             this.inputEl().on('blur', this.doForce, this);
13665         }
13666         
13667         if(this.multiple){
13668             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13669             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13670         }
13671     },
13672     
13673     initTickableEvents: function()
13674     {   
13675         this.createList();
13676         
13677         if(this.hiddenName){
13678             
13679             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13680             
13681             this.hiddenField.dom.value =
13682                 this.hiddenValue !== undefined ? this.hiddenValue :
13683                 this.value !== undefined ? this.value : '';
13684
13685             // prevent input submission
13686             this.el.dom.removeAttribute('name');
13687             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13688              
13689              
13690         }
13691         
13692 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13693         
13694         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13695         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13696         if(this.triggerList){
13697             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13698         }
13699          
13700         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13701         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13702         
13703         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13704         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13705         
13706         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13707         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13708         
13709         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13710         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13711         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13712         
13713         this.okBtn.hide();
13714         this.cancelBtn.hide();
13715         
13716         var _this = this;
13717         
13718         (function(){
13719             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13720             _this.list.setWidth(lw);
13721         }).defer(100);
13722         
13723         this.list.on('mouseover', this.onViewOver, this);
13724         this.list.on('mousemove', this.onViewMove, this);
13725         
13726         this.list.on('scroll', this.onViewScroll, this);
13727         
13728         if(!this.tpl){
13729             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13730                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13731         }
13732
13733         this.view = new Roo.View(this.list, this.tpl, {
13734             singleSelect:true,
13735             tickable:true,
13736             parent:this,
13737             store: this.store,
13738             selectedClass: this.selectedClass
13739         });
13740         
13741         //this.view.wrapEl.setDisplayed(false);
13742         this.view.on('click', this.onViewClick, this);
13743         
13744         
13745         
13746         this.store.on('beforeload', this.onBeforeLoad, this);
13747         this.store.on('load', this.onLoad, this);
13748         this.store.on('loadexception', this.onLoadException, this);
13749         
13750         if(this.editable){
13751             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13752                 "up" : function(e){
13753                     this.inKeyMode = true;
13754                     this.selectPrev();
13755                 },
13756
13757                 "down" : function(e){
13758                     this.inKeyMode = true;
13759                     this.selectNext();
13760                 },
13761
13762                 "enter" : function(e){
13763                     if(this.fireEvent("specialkey", this, e)){
13764                         this.onViewClick(false);
13765                     }
13766                     
13767                     return true;
13768                 },
13769
13770                 "esc" : function(e){
13771                     this.onTickableFooterButtonClick(e, false, false);
13772                 },
13773
13774                 "tab" : function(e){
13775                     this.fireEvent("specialkey", this, e);
13776                     
13777                     this.onTickableFooterButtonClick(e, false, false);
13778                     
13779                     return true;
13780                 },
13781
13782                 scope : this,
13783
13784                 doRelay : function(e, fn, key){
13785                     if(this.scope.isExpanded()){
13786                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13787                     }
13788                     return true;
13789                 },
13790
13791                 forceKeyDown: true
13792             });
13793         }
13794         
13795         this.queryDelay = Math.max(this.queryDelay || 10,
13796                 this.mode == 'local' ? 10 : 250);
13797         
13798         
13799         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13800         
13801         if(this.typeAhead){
13802             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13803         }
13804         
13805         if(this.editable !== false){
13806             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13807         }
13808         
13809         this.indicator = this.indicatorEl();
13810         
13811         if(this.indicator){
13812             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13813             this.indicator.hide();
13814         }
13815         
13816     },
13817
13818     onDestroy : function(){
13819         if(this.view){
13820             this.view.setStore(null);
13821             this.view.el.removeAllListeners();
13822             this.view.el.remove();
13823             this.view.purgeListeners();
13824         }
13825         if(this.list){
13826             this.list.dom.innerHTML  = '';
13827         }
13828         
13829         if(this.store){
13830             this.store.un('beforeload', this.onBeforeLoad, this);
13831             this.store.un('load', this.onLoad, this);
13832             this.store.un('loadexception', this.onLoadException, this);
13833         }
13834         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13835     },
13836
13837     // private
13838     fireKey : function(e){
13839         if(e.isNavKeyPress() && !this.list.isVisible()){
13840             this.fireEvent("specialkey", this, e);
13841         }
13842     },
13843
13844     // private
13845     onResize: function(w, h){
13846 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13847 //        
13848 //        if(typeof w != 'number'){
13849 //            // we do not handle it!?!?
13850 //            return;
13851 //        }
13852 //        var tw = this.trigger.getWidth();
13853 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13854 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13855 //        var x = w - tw;
13856 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13857 //            
13858 //        //this.trigger.setStyle('left', x+'px');
13859 //        
13860 //        if(this.list && this.listWidth === undefined){
13861 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13862 //            this.list.setWidth(lw);
13863 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13864 //        }
13865         
13866     
13867         
13868     },
13869
13870     /**
13871      * Allow or prevent the user from directly editing the field text.  If false is passed,
13872      * the user will only be able to select from the items defined in the dropdown list.  This method
13873      * is the runtime equivalent of setting the 'editable' config option at config time.
13874      * @param {Boolean} value True to allow the user to directly edit the field text
13875      */
13876     setEditable : function(value){
13877         if(value == this.editable){
13878             return;
13879         }
13880         this.editable = value;
13881         if(!value){
13882             this.inputEl().dom.setAttribute('readOnly', true);
13883             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13884             this.inputEl().addClass('x-combo-noedit');
13885         }else{
13886             this.inputEl().dom.setAttribute('readOnly', false);
13887             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13888             this.inputEl().removeClass('x-combo-noedit');
13889         }
13890     },
13891
13892     // private
13893     
13894     onBeforeLoad : function(combo,opts){
13895         if(!this.hasFocus){
13896             return;
13897         }
13898          if (!opts.add) {
13899             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13900          }
13901         this.restrictHeight();
13902         this.selectedIndex = -1;
13903     },
13904
13905     // private
13906     onLoad : function(){
13907         
13908         this.hasQuery = false;
13909         
13910         if(!this.hasFocus){
13911             return;
13912         }
13913         
13914         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13915             this.loading.hide();
13916         }
13917         
13918         if(this.store.getCount() > 0){
13919             
13920             this.expand();
13921             this.restrictHeight();
13922             if(this.lastQuery == this.allQuery){
13923                 if(this.editable && !this.tickable){
13924                     this.inputEl().dom.select();
13925                 }
13926                 
13927                 if(
13928                     !this.selectByValue(this.value, true) &&
13929                     this.autoFocus && 
13930                     (
13931                         !this.store.lastOptions ||
13932                         typeof(this.store.lastOptions.add) == 'undefined' || 
13933                         this.store.lastOptions.add != true
13934                     )
13935                 ){
13936                     this.select(0, true);
13937                 }
13938             }else{
13939                 if(this.autoFocus){
13940                     this.selectNext();
13941                 }
13942                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13943                     this.taTask.delay(this.typeAheadDelay);
13944                 }
13945             }
13946         }else{
13947             this.onEmptyResults();
13948         }
13949         
13950         //this.el.focus();
13951     },
13952     // private
13953     onLoadException : function()
13954     {
13955         this.hasQuery = false;
13956         
13957         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13958             this.loading.hide();
13959         }
13960         
13961         if(this.tickable && this.editable){
13962             return;
13963         }
13964         
13965         this.collapse();
13966         // only causes errors at present
13967         //Roo.log(this.store.reader.jsonData);
13968         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13969             // fixme
13970             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13971         //}
13972         
13973         
13974     },
13975     // private
13976     onTypeAhead : function(){
13977         if(this.store.getCount() > 0){
13978             var r = this.store.getAt(0);
13979             var newValue = r.data[this.displayField];
13980             var len = newValue.length;
13981             var selStart = this.getRawValue().length;
13982             
13983             if(selStart != len){
13984                 this.setRawValue(newValue);
13985                 this.selectText(selStart, newValue.length);
13986             }
13987         }
13988     },
13989
13990     // private
13991     onSelect : function(record, index){
13992         
13993         if(this.fireEvent('beforeselect', this, record, index) !== false){
13994         
13995             this.setFromData(index > -1 ? record.data : false);
13996             
13997             this.collapse();
13998             this.fireEvent('select', this, record, index);
13999         }
14000     },
14001
14002     /**
14003      * Returns the currently selected field value or empty string if no value is set.
14004      * @return {String} value The selected value
14005      */
14006     getValue : function()
14007     {
14008         if(Roo.isIOS && this.useNativeIOS){
14009             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14010         }
14011         
14012         if(this.multiple){
14013             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14014         }
14015         
14016         if(this.valueField){
14017             return typeof this.value != 'undefined' ? this.value : '';
14018         }else{
14019             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14020         }
14021     },
14022     
14023     getRawValue : function()
14024     {
14025         if(Roo.isIOS && this.useNativeIOS){
14026             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14027         }
14028         
14029         var v = this.inputEl().getValue();
14030         
14031         return v;
14032     },
14033
14034     /**
14035      * Clears any text/value currently set in the field
14036      */
14037     clearValue : function(){
14038         
14039         if(this.hiddenField){
14040             this.hiddenField.dom.value = '';
14041         }
14042         this.value = '';
14043         this.setRawValue('');
14044         this.lastSelectionText = '';
14045         this.lastData = false;
14046         
14047         var close = this.closeTriggerEl();
14048         
14049         if(close){
14050             close.hide();
14051         }
14052         
14053         this.validate();
14054         
14055     },
14056
14057     /**
14058      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14059      * will be displayed in the field.  If the value does not match the data value of an existing item,
14060      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14061      * Otherwise the field will be blank (although the value will still be set).
14062      * @param {String} value The value to match
14063      */
14064     setValue : function(v)
14065     {
14066         if(Roo.isIOS && this.useNativeIOS){
14067             this.setIOSValue(v);
14068             return;
14069         }
14070         
14071         if(this.multiple){
14072             this.syncValue();
14073             return;
14074         }
14075         
14076         var text = v;
14077         if(this.valueField){
14078             var r = this.findRecord(this.valueField, v);
14079             if(r){
14080                 text = r.data[this.displayField];
14081             }else if(this.valueNotFoundText !== undefined){
14082                 text = this.valueNotFoundText;
14083             }
14084         }
14085         this.lastSelectionText = text;
14086         if(this.hiddenField){
14087             this.hiddenField.dom.value = v;
14088         }
14089         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14090         this.value = v;
14091         
14092         var close = this.closeTriggerEl();
14093         
14094         if(close){
14095             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14096         }
14097         
14098         this.validate();
14099     },
14100     /**
14101      * @property {Object} the last set data for the element
14102      */
14103     
14104     lastData : false,
14105     /**
14106      * Sets the value of the field based on a object which is related to the record format for the store.
14107      * @param {Object} value the value to set as. or false on reset?
14108      */
14109     setFromData : function(o){
14110         
14111         if(this.multiple){
14112             this.addItem(o);
14113             return;
14114         }
14115             
14116         var dv = ''; // display value
14117         var vv = ''; // value value..
14118         this.lastData = o;
14119         if (this.displayField) {
14120             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14121         } else {
14122             // this is an error condition!!!
14123             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14124         }
14125         
14126         if(this.valueField){
14127             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14128         }
14129         
14130         var close = this.closeTriggerEl();
14131         
14132         if(close){
14133             if(dv.length || vv * 1 > 0){
14134                 close.show() ;
14135                 this.blockFocus=true;
14136             } else {
14137                 close.hide();
14138             }             
14139         }
14140         
14141         if(this.hiddenField){
14142             this.hiddenField.dom.value = vv;
14143             
14144             this.lastSelectionText = dv;
14145             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14146             this.value = vv;
14147             return;
14148         }
14149         // no hidden field.. - we store the value in 'value', but still display
14150         // display field!!!!
14151         this.lastSelectionText = dv;
14152         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14153         this.value = vv;
14154         
14155         
14156         
14157     },
14158     // private
14159     reset : function(){
14160         // overridden so that last data is reset..
14161         
14162         if(this.multiple){
14163             this.clearItem();
14164             return;
14165         }
14166         
14167         this.setValue(this.originalValue);
14168         //this.clearInvalid();
14169         this.lastData = false;
14170         if (this.view) {
14171             this.view.clearSelections();
14172         }
14173         
14174         this.validate();
14175     },
14176     // private
14177     findRecord : function(prop, value){
14178         var record;
14179         if(this.store.getCount() > 0){
14180             this.store.each(function(r){
14181                 if(r.data[prop] == value){
14182                     record = r;
14183                     return false;
14184                 }
14185                 return true;
14186             });
14187         }
14188         return record;
14189     },
14190     
14191     getName: function()
14192     {
14193         // returns hidden if it's set..
14194         if (!this.rendered) {return ''};
14195         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14196         
14197     },
14198     // private
14199     onViewMove : function(e, t){
14200         this.inKeyMode = false;
14201     },
14202
14203     // private
14204     onViewOver : function(e, t){
14205         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14206             return;
14207         }
14208         var item = this.view.findItemFromChild(t);
14209         
14210         if(item){
14211             var index = this.view.indexOf(item);
14212             this.select(index, false);
14213         }
14214     },
14215
14216     // private
14217     onViewClick : function(view, doFocus, el, e)
14218     {
14219         var index = this.view.getSelectedIndexes()[0];
14220         
14221         var r = this.store.getAt(index);
14222         
14223         if(this.tickable){
14224             
14225             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14226                 return;
14227             }
14228             
14229             var rm = false;
14230             var _this = this;
14231             
14232             Roo.each(this.tickItems, function(v,k){
14233                 
14234                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14235                     Roo.log(v);
14236                     _this.tickItems.splice(k, 1);
14237                     
14238                     if(typeof(e) == 'undefined' && view == false){
14239                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14240                     }
14241                     
14242                     rm = true;
14243                     return;
14244                 }
14245             });
14246             
14247             if(rm){
14248                 return;
14249             }
14250             
14251             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14252                 this.tickItems.push(r.data);
14253             }
14254             
14255             if(typeof(e) == 'undefined' && view == false){
14256                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14257             }
14258                     
14259             return;
14260         }
14261         
14262         if(r){
14263             this.onSelect(r, index);
14264         }
14265         if(doFocus !== false && !this.blockFocus){
14266             this.inputEl().focus();
14267         }
14268     },
14269
14270     // private
14271     restrictHeight : function(){
14272         //this.innerList.dom.style.height = '';
14273         //var inner = this.innerList.dom;
14274         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14275         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14276         //this.list.beginUpdate();
14277         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14278         this.list.alignTo(this.inputEl(), this.listAlign);
14279         this.list.alignTo(this.inputEl(), this.listAlign);
14280         //this.list.endUpdate();
14281     },
14282
14283     // private
14284     onEmptyResults : function(){
14285         
14286         if(this.tickable && this.editable){
14287             this.hasFocus = false;
14288             this.restrictHeight();
14289             return;
14290         }
14291         
14292         this.collapse();
14293     },
14294
14295     /**
14296      * Returns true if the dropdown list is expanded, else false.
14297      */
14298     isExpanded : function(){
14299         return this.list.isVisible();
14300     },
14301
14302     /**
14303      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14304      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14305      * @param {String} value The data value of the item to select
14306      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14307      * selected item if it is not currently in view (defaults to true)
14308      * @return {Boolean} True if the value matched an item in the list, else false
14309      */
14310     selectByValue : function(v, scrollIntoView){
14311         if(v !== undefined && v !== null){
14312             var r = this.findRecord(this.valueField || this.displayField, v);
14313             if(r){
14314                 this.select(this.store.indexOf(r), scrollIntoView);
14315                 return true;
14316             }
14317         }
14318         return false;
14319     },
14320
14321     /**
14322      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14323      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14324      * @param {Number} index The zero-based index of the list item to select
14325      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14326      * selected item if it is not currently in view (defaults to true)
14327      */
14328     select : function(index, scrollIntoView){
14329         this.selectedIndex = index;
14330         this.view.select(index);
14331         if(scrollIntoView !== false){
14332             var el = this.view.getNode(index);
14333             /*
14334              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14335              */
14336             if(el){
14337                 this.list.scrollChildIntoView(el, false);
14338             }
14339         }
14340     },
14341
14342     // private
14343     selectNext : function(){
14344         var ct = this.store.getCount();
14345         if(ct > 0){
14346             if(this.selectedIndex == -1){
14347                 this.select(0);
14348             }else if(this.selectedIndex < ct-1){
14349                 this.select(this.selectedIndex+1);
14350             }
14351         }
14352     },
14353
14354     // private
14355     selectPrev : 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 != 0){
14361                 this.select(this.selectedIndex-1);
14362             }
14363         }
14364     },
14365
14366     // private
14367     onKeyUp : function(e){
14368         if(this.editable !== false && !e.isSpecialKey()){
14369             this.lastKey = e.getKey();
14370             this.dqTask.delay(this.queryDelay);
14371         }
14372     },
14373
14374     // private
14375     validateBlur : function(){
14376         return !this.list || !this.list.isVisible();   
14377     },
14378
14379     // private
14380     initQuery : function(){
14381         
14382         var v = this.getRawValue();
14383         
14384         if(this.tickable && this.editable){
14385             v = this.tickableInputEl().getValue();
14386         }
14387         
14388         this.doQuery(v);
14389     },
14390
14391     // private
14392     doForce : function(){
14393         if(this.inputEl().dom.value.length > 0){
14394             this.inputEl().dom.value =
14395                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14396              
14397         }
14398     },
14399
14400     /**
14401      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14402      * query allowing the query action to be canceled if needed.
14403      * @param {String} query The SQL query to execute
14404      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14405      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14406      * saved in the current store (defaults to false)
14407      */
14408     doQuery : function(q, forceAll){
14409         
14410         if(q === undefined || q === null){
14411             q = '';
14412         }
14413         var qe = {
14414             query: q,
14415             forceAll: forceAll,
14416             combo: this,
14417             cancel:false
14418         };
14419         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14420             return false;
14421         }
14422         q = qe.query;
14423         
14424         forceAll = qe.forceAll;
14425         if(forceAll === true || (q.length >= this.minChars)){
14426             
14427             this.hasQuery = true;
14428             
14429             if(this.lastQuery != q || this.alwaysQuery){
14430                 this.lastQuery = q;
14431                 if(this.mode == 'local'){
14432                     this.selectedIndex = -1;
14433                     if(forceAll){
14434                         this.store.clearFilter();
14435                     }else{
14436                         
14437                         if(this.specialFilter){
14438                             this.fireEvent('specialfilter', this);
14439                             this.onLoad();
14440                             return;
14441                         }
14442                         
14443                         this.store.filter(this.displayField, q);
14444                     }
14445                     
14446                     this.store.fireEvent("datachanged", this.store);
14447                     
14448                     this.onLoad();
14449                     
14450                     
14451                 }else{
14452                     
14453                     this.store.baseParams[this.queryParam] = q;
14454                     
14455                     var options = {params : this.getParams(q)};
14456                     
14457                     if(this.loadNext){
14458                         options.add = true;
14459                         options.params.start = this.page * this.pageSize;
14460                     }
14461                     
14462                     this.store.load(options);
14463                     
14464                     /*
14465                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14466                      *  we should expand the list on onLoad
14467                      *  so command out it
14468                      */
14469 //                    this.expand();
14470                 }
14471             }else{
14472                 this.selectedIndex = -1;
14473                 this.onLoad();   
14474             }
14475         }
14476         
14477         this.loadNext = false;
14478     },
14479     
14480     // private
14481     getParams : function(q){
14482         var p = {};
14483         //p[this.queryParam] = q;
14484         
14485         if(this.pageSize){
14486             p.start = 0;
14487             p.limit = this.pageSize;
14488         }
14489         return p;
14490     },
14491
14492     /**
14493      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14494      */
14495     collapse : function(){
14496         if(!this.isExpanded()){
14497             return;
14498         }
14499         
14500         this.list.hide();
14501         
14502         this.hasFocus = false;
14503         
14504         if(this.tickable){
14505             this.okBtn.hide();
14506             this.cancelBtn.hide();
14507             this.trigger.show();
14508             
14509             if(this.editable){
14510                 this.tickableInputEl().dom.value = '';
14511                 this.tickableInputEl().blur();
14512             }
14513             
14514         }
14515         
14516         Roo.get(document).un('mousedown', this.collapseIf, this);
14517         Roo.get(document).un('mousewheel', this.collapseIf, this);
14518         if (!this.editable) {
14519             Roo.get(document).un('keydown', this.listKeyPress, this);
14520         }
14521         this.fireEvent('collapse', this);
14522         
14523         this.validate();
14524     },
14525
14526     // private
14527     collapseIf : function(e){
14528         var in_combo  = e.within(this.el);
14529         var in_list =  e.within(this.list);
14530         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14531         
14532         if (in_combo || in_list || is_list) {
14533             //e.stopPropagation();
14534             return;
14535         }
14536         
14537         if(this.tickable){
14538             this.onTickableFooterButtonClick(e, false, false);
14539         }
14540
14541         this.collapse();
14542         
14543     },
14544
14545     /**
14546      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14547      */
14548     expand : function(){
14549        
14550         if(this.isExpanded() || !this.hasFocus){
14551             return;
14552         }
14553         
14554         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14555         this.list.setWidth(lw);
14556         
14557         Roo.log('expand');
14558         
14559         this.list.show();
14560         
14561         this.restrictHeight();
14562         
14563         if(this.tickable){
14564             
14565             this.tickItems = Roo.apply([], this.item);
14566             
14567             this.okBtn.show();
14568             this.cancelBtn.show();
14569             this.trigger.hide();
14570             
14571             if(this.editable){
14572                 this.tickableInputEl().focus();
14573             }
14574             
14575         }
14576         
14577         Roo.get(document).on('mousedown', this.collapseIf, this);
14578         Roo.get(document).on('mousewheel', this.collapseIf, this);
14579         if (!this.editable) {
14580             Roo.get(document).on('keydown', this.listKeyPress, this);
14581         }
14582         
14583         this.fireEvent('expand', this);
14584     },
14585
14586     // private
14587     // Implements the default empty TriggerField.onTriggerClick function
14588     onTriggerClick : function(e)
14589     {
14590         Roo.log('trigger click');
14591         
14592         if(this.disabled || !this.triggerList){
14593             return;
14594         }
14595         
14596         this.page = 0;
14597         this.loadNext = false;
14598         
14599         if(this.isExpanded()){
14600             this.collapse();
14601             if (!this.blockFocus) {
14602                 this.inputEl().focus();
14603             }
14604             
14605         }else {
14606             this.hasFocus = true;
14607             if(this.triggerAction == 'all') {
14608                 this.doQuery(this.allQuery, true);
14609             } else {
14610                 this.doQuery(this.getRawValue());
14611             }
14612             if (!this.blockFocus) {
14613                 this.inputEl().focus();
14614             }
14615         }
14616     },
14617     
14618     onTickableTriggerClick : function(e)
14619     {
14620         if(this.disabled){
14621             return;
14622         }
14623         
14624         this.page = 0;
14625         this.loadNext = false;
14626         this.hasFocus = true;
14627         
14628         if(this.triggerAction == 'all') {
14629             this.doQuery(this.allQuery, true);
14630         } else {
14631             this.doQuery(this.getRawValue());
14632         }
14633     },
14634     
14635     onSearchFieldClick : function(e)
14636     {
14637         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14638             this.onTickableFooterButtonClick(e, false, false);
14639             return;
14640         }
14641         
14642         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14643             return;
14644         }
14645         
14646         this.page = 0;
14647         this.loadNext = false;
14648         this.hasFocus = true;
14649         
14650         if(this.triggerAction == 'all') {
14651             this.doQuery(this.allQuery, true);
14652         } else {
14653             this.doQuery(this.getRawValue());
14654         }
14655     },
14656     
14657     listKeyPress : function(e)
14658     {
14659         //Roo.log('listkeypress');
14660         // scroll to first matching element based on key pres..
14661         if (e.isSpecialKey()) {
14662             return false;
14663         }
14664         var k = String.fromCharCode(e.getKey()).toUpperCase();
14665         //Roo.log(k);
14666         var match  = false;
14667         var csel = this.view.getSelectedNodes();
14668         var cselitem = false;
14669         if (csel.length) {
14670             var ix = this.view.indexOf(csel[0]);
14671             cselitem  = this.store.getAt(ix);
14672             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14673                 cselitem = false;
14674             }
14675             
14676         }
14677         
14678         this.store.each(function(v) { 
14679             if (cselitem) {
14680                 // start at existing selection.
14681                 if (cselitem.id == v.id) {
14682                     cselitem = false;
14683                 }
14684                 return true;
14685             }
14686                 
14687             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14688                 match = this.store.indexOf(v);
14689                 return false;
14690             }
14691             return true;
14692         }, this);
14693         
14694         if (match === false) {
14695             return true; // no more action?
14696         }
14697         // scroll to?
14698         this.view.select(match);
14699         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14700         sn.scrollIntoView(sn.dom.parentNode, false);
14701     },
14702     
14703     onViewScroll : function(e, t){
14704         
14705         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){
14706             return;
14707         }
14708         
14709         this.hasQuery = true;
14710         
14711         this.loading = this.list.select('.loading', true).first();
14712         
14713         if(this.loading === null){
14714             this.list.createChild({
14715                 tag: 'div',
14716                 cls: 'loading roo-select2-more-results roo-select2-active',
14717                 html: 'Loading more results...'
14718             });
14719             
14720             this.loading = this.list.select('.loading', true).first();
14721             
14722             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14723             
14724             this.loading.hide();
14725         }
14726         
14727         this.loading.show();
14728         
14729         var _combo = this;
14730         
14731         this.page++;
14732         this.loadNext = true;
14733         
14734         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14735         
14736         return;
14737     },
14738     
14739     addItem : function(o)
14740     {   
14741         var dv = ''; // display value
14742         
14743         if (this.displayField) {
14744             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14745         } else {
14746             // this is an error condition!!!
14747             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14748         }
14749         
14750         if(!dv.length){
14751             return;
14752         }
14753         
14754         var choice = this.choices.createChild({
14755             tag: 'li',
14756             cls: 'roo-select2-search-choice',
14757             cn: [
14758                 {
14759                     tag: 'div',
14760                     html: dv
14761                 },
14762                 {
14763                     tag: 'a',
14764                     href: '#',
14765                     cls: 'roo-select2-search-choice-close fa fa-times',
14766                     tabindex: '-1'
14767                 }
14768             ]
14769             
14770         }, this.searchField);
14771         
14772         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14773         
14774         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14775         
14776         this.item.push(o);
14777         
14778         this.lastData = o;
14779         
14780         this.syncValue();
14781         
14782         this.inputEl().dom.value = '';
14783         
14784         this.validate();
14785     },
14786     
14787     onRemoveItem : function(e, _self, o)
14788     {
14789         e.preventDefault();
14790         
14791         this.lastItem = Roo.apply([], this.item);
14792         
14793         var index = this.item.indexOf(o.data) * 1;
14794         
14795         if( index < 0){
14796             Roo.log('not this item?!');
14797             return;
14798         }
14799         
14800         this.item.splice(index, 1);
14801         o.item.remove();
14802         
14803         this.syncValue();
14804         
14805         this.fireEvent('remove', this, e);
14806         
14807         this.validate();
14808         
14809     },
14810     
14811     syncValue : function()
14812     {
14813         if(!this.item.length){
14814             this.clearValue();
14815             return;
14816         }
14817             
14818         var value = [];
14819         var _this = this;
14820         Roo.each(this.item, function(i){
14821             if(_this.valueField){
14822                 value.push(i[_this.valueField]);
14823                 return;
14824             }
14825
14826             value.push(i);
14827         });
14828
14829         this.value = value.join(',');
14830
14831         if(this.hiddenField){
14832             this.hiddenField.dom.value = this.value;
14833         }
14834         
14835         this.store.fireEvent("datachanged", this.store);
14836         
14837         this.validate();
14838     },
14839     
14840     clearItem : function()
14841     {
14842         if(!this.multiple){
14843             return;
14844         }
14845         
14846         this.item = [];
14847         
14848         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14849            c.remove();
14850         });
14851         
14852         this.syncValue();
14853         
14854         this.validate();
14855         
14856         if(this.tickable && !Roo.isTouch){
14857             this.view.refresh();
14858         }
14859     },
14860     
14861     inputEl: function ()
14862     {
14863         if(Roo.isIOS && this.useNativeIOS){
14864             return this.el.select('select.roo-ios-select', true).first();
14865         }
14866         
14867         if(Roo.isTouch && this.mobileTouchView){
14868             return this.el.select('input.form-control',true).first();
14869         }
14870         
14871         if(this.tickable){
14872             return this.searchField;
14873         }
14874         
14875         return this.el.select('input.form-control',true).first();
14876     },
14877     
14878     onTickableFooterButtonClick : function(e, btn, el)
14879     {
14880         e.preventDefault();
14881         
14882         this.lastItem = Roo.apply([], this.item);
14883         
14884         if(btn && btn.name == 'cancel'){
14885             this.tickItems = Roo.apply([], this.item);
14886             this.collapse();
14887             return;
14888         }
14889         
14890         this.clearItem();
14891         
14892         var _this = this;
14893         
14894         Roo.each(this.tickItems, function(o){
14895             _this.addItem(o);
14896         });
14897         
14898         this.collapse();
14899         
14900     },
14901     
14902     validate : function()
14903     {
14904         if(this.getVisibilityEl().hasClass('hidden')){
14905             return true;
14906         }
14907         
14908         var v = this.getRawValue();
14909         
14910         if(this.multiple){
14911             v = this.getValue();
14912         }
14913         
14914         if(this.disabled || this.allowBlank || v.length){
14915             this.markValid();
14916             return true;
14917         }
14918         
14919         this.markInvalid();
14920         return false;
14921     },
14922     
14923     tickableInputEl : function()
14924     {
14925         if(!this.tickable || !this.editable){
14926             return this.inputEl();
14927         }
14928         
14929         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14930     },
14931     
14932     
14933     getAutoCreateTouchView : function()
14934     {
14935         var id = Roo.id();
14936         
14937         var cfg = {
14938             cls: 'form-group' //input-group
14939         };
14940         
14941         var input =  {
14942             tag: 'input',
14943             id : id,
14944             type : this.inputType,
14945             cls : 'form-control x-combo-noedit',
14946             autocomplete: 'new-password',
14947             placeholder : this.placeholder || '',
14948             readonly : true
14949         };
14950         
14951         if (this.name) {
14952             input.name = this.name;
14953         }
14954         
14955         if (this.size) {
14956             input.cls += ' input-' + this.size;
14957         }
14958         
14959         if (this.disabled) {
14960             input.disabled = true;
14961         }
14962         
14963         var inputblock = {
14964             cls : '',
14965             cn : [
14966                 input
14967             ]
14968         };
14969         
14970         if(this.before){
14971             inputblock.cls += ' input-group';
14972             
14973             inputblock.cn.unshift({
14974                 tag :'span',
14975                 cls : 'input-group-addon',
14976                 html : this.before
14977             });
14978         }
14979         
14980         if(this.removable && !this.multiple){
14981             inputblock.cls += ' roo-removable';
14982             
14983             inputblock.cn.push({
14984                 tag: 'button',
14985                 html : 'x',
14986                 cls : 'roo-combo-removable-btn close'
14987             });
14988         }
14989
14990         if(this.hasFeedback && !this.allowBlank){
14991             
14992             inputblock.cls += ' has-feedback';
14993             
14994             inputblock.cn.push({
14995                 tag: 'span',
14996                 cls: 'glyphicon form-control-feedback'
14997             });
14998             
14999         }
15000         
15001         if (this.after) {
15002             
15003             inputblock.cls += (this.before) ? '' : ' input-group';
15004             
15005             inputblock.cn.push({
15006                 tag :'span',
15007                 cls : 'input-group-addon',
15008                 html : this.after
15009             });
15010         }
15011
15012         var box = {
15013             tag: 'div',
15014             cn: [
15015                 {
15016                     tag: 'input',
15017                     type : 'hidden',
15018                     cls: 'form-hidden-field'
15019                 },
15020                 inputblock
15021             ]
15022             
15023         };
15024         
15025         if(this.multiple){
15026             box = {
15027                 tag: 'div',
15028                 cn: [
15029                     {
15030                         tag: 'input',
15031                         type : 'hidden',
15032                         cls: 'form-hidden-field'
15033                     },
15034                     {
15035                         tag: 'ul',
15036                         cls: 'roo-select2-choices',
15037                         cn:[
15038                             {
15039                                 tag: 'li',
15040                                 cls: 'roo-select2-search-field',
15041                                 cn: [
15042
15043                                     inputblock
15044                                 ]
15045                             }
15046                         ]
15047                     }
15048                 ]
15049             }
15050         };
15051         
15052         var combobox = {
15053             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15054             cn: [
15055                 box
15056             ]
15057         };
15058         
15059         if(!this.multiple && this.showToggleBtn){
15060             
15061             var caret = {
15062                         tag: 'span',
15063                         cls: 'caret'
15064             };
15065             
15066             if (this.caret != false) {
15067                 caret = {
15068                      tag: 'i',
15069                      cls: 'fa fa-' + this.caret
15070                 };
15071                 
15072             }
15073             
15074             combobox.cn.push({
15075                 tag :'span',
15076                 cls : 'input-group-addon btn dropdown-toggle',
15077                 cn : [
15078                     caret,
15079                     {
15080                         tag: 'span',
15081                         cls: 'combobox-clear',
15082                         cn  : [
15083                             {
15084                                 tag : 'i',
15085                                 cls: 'icon-remove'
15086                             }
15087                         ]
15088                     }
15089                 ]
15090
15091             })
15092         }
15093         
15094         if(this.multiple){
15095             combobox.cls += ' roo-select2-container-multi';
15096         }
15097         
15098         var align = this.labelAlign || this.parentLabelAlign();
15099         
15100         if (align ==='left' && this.fieldLabel.length) {
15101
15102             cfg.cn = [
15103                 {
15104                    tag : 'i',
15105                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15106                    tooltip : 'This field is required'
15107                 },
15108                 {
15109                     tag: 'label',
15110                     cls : 'control-label',
15111                     html : this.fieldLabel
15112
15113                 },
15114                 {
15115                     cls : '', 
15116                     cn: [
15117                         combobox
15118                     ]
15119                 }
15120             ];
15121             
15122             var labelCfg = cfg.cn[1];
15123             var contentCfg = cfg.cn[2];
15124             
15125
15126             if(this.indicatorpos == 'right'){
15127                 cfg.cn = [
15128                     {
15129                         tag: 'label',
15130                         'for' :  id,
15131                         cls : 'control-label',
15132                         cn : [
15133                             {
15134                                 tag : 'span',
15135                                 html : this.fieldLabel
15136                             },
15137                             {
15138                                 tag : 'i',
15139                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15140                                 tooltip : 'This field is required'
15141                             }
15142                         ]
15143                     },
15144                     {
15145                         cls : "",
15146                         cn: [
15147                             combobox
15148                         ]
15149                     }
15150
15151                 ];
15152                 
15153                 labelCfg = cfg.cn[0];
15154                 contentCfg = cfg.cn[1];
15155             }
15156             
15157            
15158             
15159             if(this.labelWidth > 12){
15160                 labelCfg.style = "width: " + this.labelWidth + 'px';
15161             }
15162             
15163             if(this.labelWidth < 13 && this.labelmd == 0){
15164                 this.labelmd = this.labelWidth;
15165             }
15166             
15167             if(this.labellg > 0){
15168                 labelCfg.cls += ' col-lg-' + this.labellg;
15169                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15170             }
15171             
15172             if(this.labelmd > 0){
15173                 labelCfg.cls += ' col-md-' + this.labelmd;
15174                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15175             }
15176             
15177             if(this.labelsm > 0){
15178                 labelCfg.cls += ' col-sm-' + this.labelsm;
15179                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15180             }
15181             
15182             if(this.labelxs > 0){
15183                 labelCfg.cls += ' col-xs-' + this.labelxs;
15184                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15185             }
15186                 
15187                 
15188         } else if ( this.fieldLabel.length) {
15189             cfg.cn = [
15190                 {
15191                    tag : 'i',
15192                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15193                    tooltip : 'This field is required'
15194                 },
15195                 {
15196                     tag: 'label',
15197                     cls : 'control-label',
15198                     html : this.fieldLabel
15199
15200                 },
15201                 {
15202                     cls : '', 
15203                     cn: [
15204                         combobox
15205                     ]
15206                 }
15207             ];
15208             
15209             if(this.indicatorpos == 'right'){
15210                 cfg.cn = [
15211                     {
15212                         tag: 'label',
15213                         cls : 'control-label',
15214                         html : this.fieldLabel,
15215                         cn : [
15216                             {
15217                                tag : 'i',
15218                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15219                                tooltip : 'This field is required'
15220                             }
15221                         ]
15222                     },
15223                     {
15224                         cls : '', 
15225                         cn: [
15226                             combobox
15227                         ]
15228                     }
15229                 ];
15230             }
15231         } else {
15232             cfg.cn = combobox;    
15233         }
15234         
15235         
15236         var settings = this;
15237         
15238         ['xs','sm','md','lg'].map(function(size){
15239             if (settings[size]) {
15240                 cfg.cls += ' col-' + size + '-' + settings[size];
15241             }
15242         });
15243         
15244         return cfg;
15245     },
15246     
15247     initTouchView : function()
15248     {
15249         this.renderTouchView();
15250         
15251         this.touchViewEl.on('scroll', function(){
15252             this.el.dom.scrollTop = 0;
15253         }, this);
15254         
15255         this.originalValue = this.getValue();
15256         
15257         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15258         
15259         this.inputEl().on("click", this.showTouchView, this);
15260         if (this.triggerEl) {
15261             this.triggerEl.on("click", this.showTouchView, this);
15262         }
15263         
15264         
15265         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15266         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15267         
15268         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15269         
15270         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15271         this.store.on('load', this.onTouchViewLoad, this);
15272         this.store.on('loadexception', this.onTouchViewLoadException, this);
15273         
15274         if(this.hiddenName){
15275             
15276             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15277             
15278             this.hiddenField.dom.value =
15279                 this.hiddenValue !== undefined ? this.hiddenValue :
15280                 this.value !== undefined ? this.value : '';
15281         
15282             this.el.dom.removeAttribute('name');
15283             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15284         }
15285         
15286         if(this.multiple){
15287             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15288             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15289         }
15290         
15291         if(this.removable && !this.multiple){
15292             var close = this.closeTriggerEl();
15293             if(close){
15294                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15295                 close.on('click', this.removeBtnClick, this, close);
15296             }
15297         }
15298         /*
15299          * fix the bug in Safari iOS8
15300          */
15301         this.inputEl().on("focus", function(e){
15302             document.activeElement.blur();
15303         }, this);
15304         
15305         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15306         
15307         return;
15308         
15309         
15310     },
15311     
15312     renderTouchView : function()
15313     {
15314         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15315         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15316         
15317         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15318         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15319         
15320         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15321         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15322         this.touchViewBodyEl.setStyle('overflow', 'auto');
15323         
15324         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15325         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15326         
15327         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15328         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15329         
15330     },
15331     
15332     showTouchView : function()
15333     {
15334         if(this.disabled){
15335             return;
15336         }
15337         
15338         this.touchViewHeaderEl.hide();
15339
15340         if(this.modalTitle.length){
15341             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15342             this.touchViewHeaderEl.show();
15343         }
15344
15345         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15346         this.touchViewEl.show();
15347
15348         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15349         
15350         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15351         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15352
15353         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15354
15355         if(this.modalTitle.length){
15356             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15357         }
15358         
15359         this.touchViewBodyEl.setHeight(bodyHeight);
15360
15361         if(this.animate){
15362             var _this = this;
15363             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15364         }else{
15365             this.touchViewEl.addClass('in');
15366         }
15367         
15368         if(this._touchViewMask){
15369             Roo.get(document.body).addClass("x-body-masked");
15370             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15371             this._touchViewMask.setStyle('z-index', 10000);
15372             this._touchViewMask.addClass('show');
15373         }
15374         
15375         this.doTouchViewQuery();
15376         
15377     },
15378     
15379     hideTouchView : function()
15380     {
15381         this.touchViewEl.removeClass('in');
15382
15383         if(this.animate){
15384             var _this = this;
15385             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15386         }else{
15387             this.touchViewEl.setStyle('display', 'none');
15388         }
15389         
15390         if(this._touchViewMask){
15391             this._touchViewMask.removeClass('show');
15392             Roo.get(document.body).removeClass("x-body-masked");
15393         }
15394     },
15395     
15396     setTouchViewValue : function()
15397     {
15398         if(this.multiple){
15399             this.clearItem();
15400         
15401             var _this = this;
15402
15403             Roo.each(this.tickItems, function(o){
15404                 this.addItem(o);
15405             }, this);
15406         }
15407         
15408         this.hideTouchView();
15409     },
15410     
15411     doTouchViewQuery : function()
15412     {
15413         var qe = {
15414             query: '',
15415             forceAll: true,
15416             combo: this,
15417             cancel:false
15418         };
15419         
15420         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15421             return false;
15422         }
15423         
15424         if(!this.alwaysQuery || this.mode == 'local'){
15425             this.onTouchViewLoad();
15426             return;
15427         }
15428         
15429         this.store.load();
15430     },
15431     
15432     onTouchViewBeforeLoad : function(combo,opts)
15433     {
15434         return;
15435     },
15436
15437     // private
15438     onTouchViewLoad : function()
15439     {
15440         if(this.store.getCount() < 1){
15441             this.onTouchViewEmptyResults();
15442             return;
15443         }
15444         
15445         this.clearTouchView();
15446         
15447         var rawValue = this.getRawValue();
15448         
15449         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15450         
15451         this.tickItems = [];
15452         
15453         this.store.data.each(function(d, rowIndex){
15454             var row = this.touchViewListGroup.createChild(template);
15455             
15456             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15457                 row.addClass(d.data.cls);
15458             }
15459             
15460             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15461                 var cfg = {
15462                     data : d.data,
15463                     html : d.data[this.displayField]
15464                 };
15465                 
15466                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15467                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15468                 }
15469             }
15470             row.removeClass('selected');
15471             if(!this.multiple && this.valueField &&
15472                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15473             {
15474                 // radio buttons..
15475                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15476                 row.addClass('selected');
15477             }
15478             
15479             if(this.multiple && this.valueField &&
15480                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15481             {
15482                 
15483                 // checkboxes...
15484                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15485                 this.tickItems.push(d.data);
15486             }
15487             
15488             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15489             
15490         }, this);
15491         
15492         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15493         
15494         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15495
15496         if(this.modalTitle.length){
15497             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15498         }
15499
15500         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15501         
15502         if(this.mobile_restrict_height && listHeight < bodyHeight){
15503             this.touchViewBodyEl.setHeight(listHeight);
15504         }
15505         
15506         var _this = this;
15507         
15508         if(firstChecked && listHeight > bodyHeight){
15509             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15510         }
15511         
15512     },
15513     
15514     onTouchViewLoadException : function()
15515     {
15516         this.hideTouchView();
15517     },
15518     
15519     onTouchViewEmptyResults : function()
15520     {
15521         this.clearTouchView();
15522         
15523         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15524         
15525         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15526         
15527     },
15528     
15529     clearTouchView : function()
15530     {
15531         this.touchViewListGroup.dom.innerHTML = '';
15532     },
15533     
15534     onTouchViewClick : function(e, el, o)
15535     {
15536         e.preventDefault();
15537         
15538         var row = o.row;
15539         var rowIndex = o.rowIndex;
15540         
15541         var r = this.store.getAt(rowIndex);
15542         
15543         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15544             
15545             if(!this.multiple){
15546                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15547                     c.dom.removeAttribute('checked');
15548                 }, this);
15549
15550                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15551
15552                 this.setFromData(r.data);
15553
15554                 var close = this.closeTriggerEl();
15555
15556                 if(close){
15557                     close.show();
15558                 }
15559
15560                 this.hideTouchView();
15561
15562                 this.fireEvent('select', this, r, rowIndex);
15563
15564                 return;
15565             }
15566
15567             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15568                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15569                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15570                 return;
15571             }
15572
15573             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15574             this.addItem(r.data);
15575             this.tickItems.push(r.data);
15576         }
15577     },
15578     
15579     getAutoCreateNativeIOS : function()
15580     {
15581         var cfg = {
15582             cls: 'form-group' //input-group,
15583         };
15584         
15585         var combobox =  {
15586             tag: 'select',
15587             cls : 'roo-ios-select'
15588         };
15589         
15590         if (this.name) {
15591             combobox.name = this.name;
15592         }
15593         
15594         if (this.disabled) {
15595             combobox.disabled = true;
15596         }
15597         
15598         var settings = this;
15599         
15600         ['xs','sm','md','lg'].map(function(size){
15601             if (settings[size]) {
15602                 cfg.cls += ' col-' + size + '-' + settings[size];
15603             }
15604         });
15605         
15606         cfg.cn = combobox;
15607         
15608         return cfg;
15609         
15610     },
15611     
15612     initIOSView : function()
15613     {
15614         this.store.on('load', this.onIOSViewLoad, this);
15615         
15616         return;
15617     },
15618     
15619     onIOSViewLoad : function()
15620     {
15621         if(this.store.getCount() < 1){
15622             return;
15623         }
15624         
15625         this.clearIOSView();
15626         
15627         if(this.allowBlank) {
15628             
15629             var default_text = '-- SELECT --';
15630             
15631             if(this.placeholder.length){
15632                 default_text = this.placeholder;
15633             }
15634             
15635             if(this.emptyTitle.length){
15636                 default_text += ' - ' + this.emptyTitle + ' -';
15637             }
15638             
15639             var opt = this.inputEl().createChild({
15640                 tag: 'option',
15641                 value : 0,
15642                 html : default_text
15643             });
15644             
15645             var o = {};
15646             o[this.valueField] = 0;
15647             o[this.displayField] = default_text;
15648             
15649             this.ios_options.push({
15650                 data : o,
15651                 el : opt
15652             });
15653             
15654         }
15655         
15656         this.store.data.each(function(d, rowIndex){
15657             
15658             var html = '';
15659             
15660             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15661                 html = d.data[this.displayField];
15662             }
15663             
15664             var value = '';
15665             
15666             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15667                 value = d.data[this.valueField];
15668             }
15669             
15670             var option = {
15671                 tag: 'option',
15672                 value : value,
15673                 html : html
15674             };
15675             
15676             if(this.value == d.data[this.valueField]){
15677                 option['selected'] = true;
15678             }
15679             
15680             var opt = this.inputEl().createChild(option);
15681             
15682             this.ios_options.push({
15683                 data : d.data,
15684                 el : opt
15685             });
15686             
15687         }, this);
15688         
15689         this.inputEl().on('change', function(){
15690            this.fireEvent('select', this);
15691         }, this);
15692         
15693     },
15694     
15695     clearIOSView: function()
15696     {
15697         this.inputEl().dom.innerHTML = '';
15698         
15699         this.ios_options = [];
15700     },
15701     
15702     setIOSValue: function(v)
15703     {
15704         this.value = v;
15705         
15706         if(!this.ios_options){
15707             return;
15708         }
15709         
15710         Roo.each(this.ios_options, function(opts){
15711            
15712            opts.el.dom.removeAttribute('selected');
15713            
15714            if(opts.data[this.valueField] != v){
15715                return;
15716            }
15717            
15718            opts.el.dom.setAttribute('selected', true);
15719            
15720         }, this);
15721     }
15722
15723     /** 
15724     * @cfg {Boolean} grow 
15725     * @hide 
15726     */
15727     /** 
15728     * @cfg {Number} growMin 
15729     * @hide 
15730     */
15731     /** 
15732     * @cfg {Number} growMax 
15733     * @hide 
15734     */
15735     /**
15736      * @hide
15737      * @method autoSize
15738      */
15739 });
15740
15741 Roo.apply(Roo.bootstrap.ComboBox,  {
15742     
15743     header : {
15744         tag: 'div',
15745         cls: 'modal-header',
15746         cn: [
15747             {
15748                 tag: 'h4',
15749                 cls: 'modal-title'
15750             }
15751         ]
15752     },
15753     
15754     body : {
15755         tag: 'div',
15756         cls: 'modal-body',
15757         cn: [
15758             {
15759                 tag: 'ul',
15760                 cls: 'list-group'
15761             }
15762         ]
15763     },
15764     
15765     listItemRadio : {
15766         tag: 'li',
15767         cls: 'list-group-item',
15768         cn: [
15769             {
15770                 tag: 'span',
15771                 cls: 'roo-combobox-list-group-item-value'
15772             },
15773             {
15774                 tag: 'div',
15775                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15776                 cn: [
15777                     {
15778                         tag: 'input',
15779                         type: 'radio'
15780                     },
15781                     {
15782                         tag: 'label'
15783                     }
15784                 ]
15785             }
15786         ]
15787     },
15788     
15789     listItemCheckbox : {
15790         tag: 'li',
15791         cls: 'list-group-item',
15792         cn: [
15793             {
15794                 tag: 'span',
15795                 cls: 'roo-combobox-list-group-item-value'
15796             },
15797             {
15798                 tag: 'div',
15799                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15800                 cn: [
15801                     {
15802                         tag: 'input',
15803                         type: 'checkbox'
15804                     },
15805                     {
15806                         tag: 'label'
15807                     }
15808                 ]
15809             }
15810         ]
15811     },
15812     
15813     emptyResult : {
15814         tag: 'div',
15815         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15816     },
15817     
15818     footer : {
15819         tag: 'div',
15820         cls: 'modal-footer',
15821         cn: [
15822             {
15823                 tag: 'div',
15824                 cls: 'row',
15825                 cn: [
15826                     {
15827                         tag: 'div',
15828                         cls: 'col-xs-6 text-left',
15829                         cn: {
15830                             tag: 'button',
15831                             cls: 'btn btn-danger roo-touch-view-cancel',
15832                             html: 'Cancel'
15833                         }
15834                     },
15835                     {
15836                         tag: 'div',
15837                         cls: 'col-xs-6 text-right',
15838                         cn: {
15839                             tag: 'button',
15840                             cls: 'btn btn-success roo-touch-view-ok',
15841                             html: 'OK'
15842                         }
15843                     }
15844                 ]
15845             }
15846         ]
15847         
15848     }
15849 });
15850
15851 Roo.apply(Roo.bootstrap.ComboBox,  {
15852     
15853     touchViewTemplate : {
15854         tag: 'div',
15855         cls: 'modal fade roo-combobox-touch-view',
15856         cn: [
15857             {
15858                 tag: 'div',
15859                 cls: 'modal-dialog',
15860                 style : 'position:fixed', // we have to fix position....
15861                 cn: [
15862                     {
15863                         tag: 'div',
15864                         cls: 'modal-content',
15865                         cn: [
15866                             Roo.bootstrap.ComboBox.header,
15867                             Roo.bootstrap.ComboBox.body,
15868                             Roo.bootstrap.ComboBox.footer
15869                         ]
15870                     }
15871                 ]
15872             }
15873         ]
15874     }
15875 });/*
15876  * Based on:
15877  * Ext JS Library 1.1.1
15878  * Copyright(c) 2006-2007, Ext JS, LLC.
15879  *
15880  * Originally Released Under LGPL - original licence link has changed is not relivant.
15881  *
15882  * Fork - LGPL
15883  * <script type="text/javascript">
15884  */
15885
15886 /**
15887  * @class Roo.View
15888  * @extends Roo.util.Observable
15889  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15890  * This class also supports single and multi selection modes. <br>
15891  * Create a data model bound view:
15892  <pre><code>
15893  var store = new Roo.data.Store(...);
15894
15895  var view = new Roo.View({
15896     el : "my-element",
15897     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15898  
15899     singleSelect: true,
15900     selectedClass: "ydataview-selected",
15901     store: store
15902  });
15903
15904  // listen for node click?
15905  view.on("click", function(vw, index, node, e){
15906  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15907  });
15908
15909  // load XML data
15910  dataModel.load("foobar.xml");
15911  </code></pre>
15912  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15913  * <br><br>
15914  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15915  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15916  * 
15917  * Note: old style constructor is still suported (container, template, config)
15918  * 
15919  * @constructor
15920  * Create a new View
15921  * @param {Object} config The config object
15922  * 
15923  */
15924 Roo.View = function(config, depreciated_tpl, depreciated_config){
15925     
15926     this.parent = false;
15927     
15928     if (typeof(depreciated_tpl) == 'undefined') {
15929         // new way.. - universal constructor.
15930         Roo.apply(this, config);
15931         this.el  = Roo.get(this.el);
15932     } else {
15933         // old format..
15934         this.el  = Roo.get(config);
15935         this.tpl = depreciated_tpl;
15936         Roo.apply(this, depreciated_config);
15937     }
15938     this.wrapEl  = this.el.wrap().wrap();
15939     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15940     
15941     
15942     if(typeof(this.tpl) == "string"){
15943         this.tpl = new Roo.Template(this.tpl);
15944     } else {
15945         // support xtype ctors..
15946         this.tpl = new Roo.factory(this.tpl, Roo);
15947     }
15948     
15949     
15950     this.tpl.compile();
15951     
15952     /** @private */
15953     this.addEvents({
15954         /**
15955          * @event beforeclick
15956          * Fires before a click is processed. Returns false to cancel the default action.
15957          * @param {Roo.View} this
15958          * @param {Number} index The index of the target node
15959          * @param {HTMLElement} node The target node
15960          * @param {Roo.EventObject} e The raw event object
15961          */
15962             "beforeclick" : true,
15963         /**
15964          * @event click
15965          * Fires when a template node is clicked.
15966          * @param {Roo.View} this
15967          * @param {Number} index The index of the target node
15968          * @param {HTMLElement} node The target node
15969          * @param {Roo.EventObject} e The raw event object
15970          */
15971             "click" : true,
15972         /**
15973          * @event dblclick
15974          * Fires when a template node is double clicked.
15975          * @param {Roo.View} this
15976          * @param {Number} index The index of the target node
15977          * @param {HTMLElement} node The target node
15978          * @param {Roo.EventObject} e The raw event object
15979          */
15980             "dblclick" : true,
15981         /**
15982          * @event contextmenu
15983          * Fires when a template node is right clicked.
15984          * @param {Roo.View} this
15985          * @param {Number} index The index of the target node
15986          * @param {HTMLElement} node The target node
15987          * @param {Roo.EventObject} e The raw event object
15988          */
15989             "contextmenu" : true,
15990         /**
15991          * @event selectionchange
15992          * Fires when the selected nodes change.
15993          * @param {Roo.View} this
15994          * @param {Array} selections Array of the selected nodes
15995          */
15996             "selectionchange" : true,
15997     
15998         /**
15999          * @event beforeselect
16000          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16001          * @param {Roo.View} this
16002          * @param {HTMLElement} node The node to be selected
16003          * @param {Array} selections Array of currently selected nodes
16004          */
16005             "beforeselect" : true,
16006         /**
16007          * @event preparedata
16008          * Fires on every row to render, to allow you to change the data.
16009          * @param {Roo.View} this
16010          * @param {Object} data to be rendered (change this)
16011          */
16012           "preparedata" : true
16013           
16014           
16015         });
16016
16017
16018
16019     this.el.on({
16020         "click": this.onClick,
16021         "dblclick": this.onDblClick,
16022         "contextmenu": this.onContextMenu,
16023         scope:this
16024     });
16025
16026     this.selections = [];
16027     this.nodes = [];
16028     this.cmp = new Roo.CompositeElementLite([]);
16029     if(this.store){
16030         this.store = Roo.factory(this.store, Roo.data);
16031         this.setStore(this.store, true);
16032     }
16033     
16034     if ( this.footer && this.footer.xtype) {
16035            
16036          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16037         
16038         this.footer.dataSource = this.store;
16039         this.footer.container = fctr;
16040         this.footer = Roo.factory(this.footer, Roo);
16041         fctr.insertFirst(this.el);
16042         
16043         // this is a bit insane - as the paging toolbar seems to detach the el..
16044 //        dom.parentNode.parentNode.parentNode
16045          // they get detached?
16046     }
16047     
16048     
16049     Roo.View.superclass.constructor.call(this);
16050     
16051     
16052 };
16053
16054 Roo.extend(Roo.View, Roo.util.Observable, {
16055     
16056      /**
16057      * @cfg {Roo.data.Store} store Data store to load data from.
16058      */
16059     store : false,
16060     
16061     /**
16062      * @cfg {String|Roo.Element} el The container element.
16063      */
16064     el : '',
16065     
16066     /**
16067      * @cfg {String|Roo.Template} tpl The template used by this View 
16068      */
16069     tpl : false,
16070     /**
16071      * @cfg {String} dataName the named area of the template to use as the data area
16072      *                          Works with domtemplates roo-name="name"
16073      */
16074     dataName: false,
16075     /**
16076      * @cfg {String} selectedClass The css class to add to selected nodes
16077      */
16078     selectedClass : "x-view-selected",
16079      /**
16080      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16081      */
16082     emptyText : "",
16083     
16084     /**
16085      * @cfg {String} text to display on mask (default Loading)
16086      */
16087     mask : false,
16088     /**
16089      * @cfg {Boolean} multiSelect Allow multiple selection
16090      */
16091     multiSelect : false,
16092     /**
16093      * @cfg {Boolean} singleSelect Allow single selection
16094      */
16095     singleSelect:  false,
16096     
16097     /**
16098      * @cfg {Boolean} toggleSelect - selecting 
16099      */
16100     toggleSelect : false,
16101     
16102     /**
16103      * @cfg {Boolean} tickable - selecting 
16104      */
16105     tickable : false,
16106     
16107     /**
16108      * Returns the element this view is bound to.
16109      * @return {Roo.Element}
16110      */
16111     getEl : function(){
16112         return this.wrapEl;
16113     },
16114     
16115     
16116
16117     /**
16118      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16119      */
16120     refresh : function(){
16121         //Roo.log('refresh');
16122         var t = this.tpl;
16123         
16124         // if we are using something like 'domtemplate', then
16125         // the what gets used is:
16126         // t.applySubtemplate(NAME, data, wrapping data..)
16127         // the outer template then get' applied with
16128         //     the store 'extra data'
16129         // and the body get's added to the
16130         //      roo-name="data" node?
16131         //      <span class='roo-tpl-{name}'></span> ?????
16132         
16133         
16134         
16135         this.clearSelections();
16136         this.el.update("");
16137         var html = [];
16138         var records = this.store.getRange();
16139         if(records.length < 1) {
16140             
16141             // is this valid??  = should it render a template??
16142             
16143             this.el.update(this.emptyText);
16144             return;
16145         }
16146         var el = this.el;
16147         if (this.dataName) {
16148             this.el.update(t.apply(this.store.meta)); //????
16149             el = this.el.child('.roo-tpl-' + this.dataName);
16150         }
16151         
16152         for(var i = 0, len = records.length; i < len; i++){
16153             var data = this.prepareData(records[i].data, i, records[i]);
16154             this.fireEvent("preparedata", this, data, i, records[i]);
16155             
16156             var d = Roo.apply({}, data);
16157             
16158             if(this.tickable){
16159                 Roo.apply(d, {'roo-id' : Roo.id()});
16160                 
16161                 var _this = this;
16162             
16163                 Roo.each(this.parent.item, function(item){
16164                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16165                         return;
16166                     }
16167                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16168                 });
16169             }
16170             
16171             html[html.length] = Roo.util.Format.trim(
16172                 this.dataName ?
16173                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16174                     t.apply(d)
16175             );
16176         }
16177         
16178         
16179         
16180         el.update(html.join(""));
16181         this.nodes = el.dom.childNodes;
16182         this.updateIndexes(0);
16183     },
16184     
16185
16186     /**
16187      * Function to override to reformat the data that is sent to
16188      * the template for each node.
16189      * DEPRICATED - use the preparedata event handler.
16190      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16191      * a JSON object for an UpdateManager bound view).
16192      */
16193     prepareData : function(data, index, record)
16194     {
16195         this.fireEvent("preparedata", this, data, index, record);
16196         return data;
16197     },
16198
16199     onUpdate : function(ds, record){
16200         // Roo.log('on update');   
16201         this.clearSelections();
16202         var index = this.store.indexOf(record);
16203         var n = this.nodes[index];
16204         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16205         n.parentNode.removeChild(n);
16206         this.updateIndexes(index, index);
16207     },
16208
16209     
16210     
16211 // --------- FIXME     
16212     onAdd : function(ds, records, index)
16213     {
16214         //Roo.log(['on Add', ds, records, index] );        
16215         this.clearSelections();
16216         if(this.nodes.length == 0){
16217             this.refresh();
16218             return;
16219         }
16220         var n = this.nodes[index];
16221         for(var i = 0, len = records.length; i < len; i++){
16222             var d = this.prepareData(records[i].data, i, records[i]);
16223             if(n){
16224                 this.tpl.insertBefore(n, d);
16225             }else{
16226                 
16227                 this.tpl.append(this.el, d);
16228             }
16229         }
16230         this.updateIndexes(index);
16231     },
16232
16233     onRemove : function(ds, record, index){
16234        // Roo.log('onRemove');
16235         this.clearSelections();
16236         var el = this.dataName  ?
16237             this.el.child('.roo-tpl-' + this.dataName) :
16238             this.el; 
16239         
16240         el.dom.removeChild(this.nodes[index]);
16241         this.updateIndexes(index);
16242     },
16243
16244     /**
16245      * Refresh an individual node.
16246      * @param {Number} index
16247      */
16248     refreshNode : function(index){
16249         this.onUpdate(this.store, this.store.getAt(index));
16250     },
16251
16252     updateIndexes : function(startIndex, endIndex){
16253         var ns = this.nodes;
16254         startIndex = startIndex || 0;
16255         endIndex = endIndex || ns.length - 1;
16256         for(var i = startIndex; i <= endIndex; i++){
16257             ns[i].nodeIndex = i;
16258         }
16259     },
16260
16261     /**
16262      * Changes the data store this view uses and refresh the view.
16263      * @param {Store} store
16264      */
16265     setStore : function(store, initial){
16266         if(!initial && this.store){
16267             this.store.un("datachanged", this.refresh);
16268             this.store.un("add", this.onAdd);
16269             this.store.un("remove", this.onRemove);
16270             this.store.un("update", this.onUpdate);
16271             this.store.un("clear", this.refresh);
16272             this.store.un("beforeload", this.onBeforeLoad);
16273             this.store.un("load", this.onLoad);
16274             this.store.un("loadexception", this.onLoad);
16275         }
16276         if(store){
16277           
16278             store.on("datachanged", this.refresh, this);
16279             store.on("add", this.onAdd, this);
16280             store.on("remove", this.onRemove, this);
16281             store.on("update", this.onUpdate, this);
16282             store.on("clear", this.refresh, this);
16283             store.on("beforeload", this.onBeforeLoad, this);
16284             store.on("load", this.onLoad, this);
16285             store.on("loadexception", this.onLoad, this);
16286         }
16287         
16288         if(store){
16289             this.refresh();
16290         }
16291     },
16292     /**
16293      * onbeforeLoad - masks the loading area.
16294      *
16295      */
16296     onBeforeLoad : function(store,opts)
16297     {
16298          //Roo.log('onBeforeLoad');   
16299         if (!opts.add) {
16300             this.el.update("");
16301         }
16302         this.el.mask(this.mask ? this.mask : "Loading" ); 
16303     },
16304     onLoad : function ()
16305     {
16306         this.el.unmask();
16307     },
16308     
16309
16310     /**
16311      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16312      * @param {HTMLElement} node
16313      * @return {HTMLElement} The template node
16314      */
16315     findItemFromChild : function(node){
16316         var el = this.dataName  ?
16317             this.el.child('.roo-tpl-' + this.dataName,true) :
16318             this.el.dom; 
16319         
16320         if(!node || node.parentNode == el){
16321                     return node;
16322             }
16323             var p = node.parentNode;
16324             while(p && p != el){
16325             if(p.parentNode == el){
16326                 return p;
16327             }
16328             p = p.parentNode;
16329         }
16330             return null;
16331     },
16332
16333     /** @ignore */
16334     onClick : function(e){
16335         var item = this.findItemFromChild(e.getTarget());
16336         if(item){
16337             var index = this.indexOf(item);
16338             if(this.onItemClick(item, index, e) !== false){
16339                 this.fireEvent("click", this, index, item, e);
16340             }
16341         }else{
16342             this.clearSelections();
16343         }
16344     },
16345
16346     /** @ignore */
16347     onContextMenu : function(e){
16348         var item = this.findItemFromChild(e.getTarget());
16349         if(item){
16350             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16351         }
16352     },
16353
16354     /** @ignore */
16355     onDblClick : function(e){
16356         var item = this.findItemFromChild(e.getTarget());
16357         if(item){
16358             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16359         }
16360     },
16361
16362     onItemClick : function(item, index, e)
16363     {
16364         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16365             return false;
16366         }
16367         if (this.toggleSelect) {
16368             var m = this.isSelected(item) ? 'unselect' : 'select';
16369             //Roo.log(m);
16370             var _t = this;
16371             _t[m](item, true, false);
16372             return true;
16373         }
16374         if(this.multiSelect || this.singleSelect){
16375             if(this.multiSelect && e.shiftKey && this.lastSelection){
16376                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16377             }else{
16378                 this.select(item, this.multiSelect && e.ctrlKey);
16379                 this.lastSelection = item;
16380             }
16381             
16382             if(!this.tickable){
16383                 e.preventDefault();
16384             }
16385             
16386         }
16387         return true;
16388     },
16389
16390     /**
16391      * Get the number of selected nodes.
16392      * @return {Number}
16393      */
16394     getSelectionCount : function(){
16395         return this.selections.length;
16396     },
16397
16398     /**
16399      * Get the currently selected nodes.
16400      * @return {Array} An array of HTMLElements
16401      */
16402     getSelectedNodes : function(){
16403         return this.selections;
16404     },
16405
16406     /**
16407      * Get the indexes of the selected nodes.
16408      * @return {Array}
16409      */
16410     getSelectedIndexes : function(){
16411         var indexes = [], s = this.selections;
16412         for(var i = 0, len = s.length; i < len; i++){
16413             indexes.push(s[i].nodeIndex);
16414         }
16415         return indexes;
16416     },
16417
16418     /**
16419      * Clear all selections
16420      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16421      */
16422     clearSelections : function(suppressEvent){
16423         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16424             this.cmp.elements = this.selections;
16425             this.cmp.removeClass(this.selectedClass);
16426             this.selections = [];
16427             if(!suppressEvent){
16428                 this.fireEvent("selectionchange", this, this.selections);
16429             }
16430         }
16431     },
16432
16433     /**
16434      * Returns true if the passed node is selected
16435      * @param {HTMLElement/Number} node The node or node index
16436      * @return {Boolean}
16437      */
16438     isSelected : function(node){
16439         var s = this.selections;
16440         if(s.length < 1){
16441             return false;
16442         }
16443         node = this.getNode(node);
16444         return s.indexOf(node) !== -1;
16445     },
16446
16447     /**
16448      * Selects nodes.
16449      * @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
16450      * @param {Boolean} keepExisting (optional) true to keep existing selections
16451      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16452      */
16453     select : function(nodeInfo, keepExisting, suppressEvent){
16454         if(nodeInfo instanceof Array){
16455             if(!keepExisting){
16456                 this.clearSelections(true);
16457             }
16458             for(var i = 0, len = nodeInfo.length; i < len; i++){
16459                 this.select(nodeInfo[i], true, true);
16460             }
16461             return;
16462         } 
16463         var node = this.getNode(nodeInfo);
16464         if(!node || this.isSelected(node)){
16465             return; // already selected.
16466         }
16467         if(!keepExisting){
16468             this.clearSelections(true);
16469         }
16470         
16471         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16472             Roo.fly(node).addClass(this.selectedClass);
16473             this.selections.push(node);
16474             if(!suppressEvent){
16475                 this.fireEvent("selectionchange", this, this.selections);
16476             }
16477         }
16478         
16479         
16480     },
16481       /**
16482      * Unselects nodes.
16483      * @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
16484      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16485      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16486      */
16487     unselect : function(nodeInfo, keepExisting, suppressEvent)
16488     {
16489         if(nodeInfo instanceof Array){
16490             Roo.each(this.selections, function(s) {
16491                 this.unselect(s, nodeInfo);
16492             }, this);
16493             return;
16494         }
16495         var node = this.getNode(nodeInfo);
16496         if(!node || !this.isSelected(node)){
16497             //Roo.log("not selected");
16498             return; // not selected.
16499         }
16500         // fireevent???
16501         var ns = [];
16502         Roo.each(this.selections, function(s) {
16503             if (s == node ) {
16504                 Roo.fly(node).removeClass(this.selectedClass);
16505
16506                 return;
16507             }
16508             ns.push(s);
16509         },this);
16510         
16511         this.selections= ns;
16512         this.fireEvent("selectionchange", this, this.selections);
16513     },
16514
16515     /**
16516      * Gets a template node.
16517      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16518      * @return {HTMLElement} The node or null if it wasn't found
16519      */
16520     getNode : function(nodeInfo){
16521         if(typeof nodeInfo == "string"){
16522             return document.getElementById(nodeInfo);
16523         }else if(typeof nodeInfo == "number"){
16524             return this.nodes[nodeInfo];
16525         }
16526         return nodeInfo;
16527     },
16528
16529     /**
16530      * Gets a range template nodes.
16531      * @param {Number} startIndex
16532      * @param {Number} endIndex
16533      * @return {Array} An array of nodes
16534      */
16535     getNodes : function(start, end){
16536         var ns = this.nodes;
16537         start = start || 0;
16538         end = typeof end == "undefined" ? ns.length - 1 : end;
16539         var nodes = [];
16540         if(start <= end){
16541             for(var i = start; i <= end; i++){
16542                 nodes.push(ns[i]);
16543             }
16544         } else{
16545             for(var i = start; i >= end; i--){
16546                 nodes.push(ns[i]);
16547             }
16548         }
16549         return nodes;
16550     },
16551
16552     /**
16553      * Finds the index of the passed node
16554      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16555      * @return {Number} The index of the node or -1
16556      */
16557     indexOf : function(node){
16558         node = this.getNode(node);
16559         if(typeof node.nodeIndex == "number"){
16560             return node.nodeIndex;
16561         }
16562         var ns = this.nodes;
16563         for(var i = 0, len = ns.length; i < len; i++){
16564             if(ns[i] == node){
16565                 return i;
16566             }
16567         }
16568         return -1;
16569     }
16570 });
16571 /*
16572  * - LGPL
16573  *
16574  * based on jquery fullcalendar
16575  * 
16576  */
16577
16578 Roo.bootstrap = Roo.bootstrap || {};
16579 /**
16580  * @class Roo.bootstrap.Calendar
16581  * @extends Roo.bootstrap.Component
16582  * Bootstrap Calendar class
16583  * @cfg {Boolean} loadMask (true|false) default false
16584  * @cfg {Object} header generate the user specific header of the calendar, default false
16585
16586  * @constructor
16587  * Create a new Container
16588  * @param {Object} config The config object
16589  */
16590
16591
16592
16593 Roo.bootstrap.Calendar = function(config){
16594     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16595      this.addEvents({
16596         /**
16597              * @event select
16598              * Fires when a date is selected
16599              * @param {DatePicker} this
16600              * @param {Date} date The selected date
16601              */
16602         'select': true,
16603         /**
16604              * @event monthchange
16605              * Fires when the displayed month changes 
16606              * @param {DatePicker} this
16607              * @param {Date} date The selected month
16608              */
16609         'monthchange': true,
16610         /**
16611              * @event evententer
16612              * Fires when mouse over an event
16613              * @param {Calendar} this
16614              * @param {event} Event
16615              */
16616         'evententer': true,
16617         /**
16618              * @event eventleave
16619              * Fires when the mouse leaves an
16620              * @param {Calendar} this
16621              * @param {event}
16622              */
16623         'eventleave': true,
16624         /**
16625              * @event eventclick
16626              * Fires when the mouse click an
16627              * @param {Calendar} this
16628              * @param {event}
16629              */
16630         'eventclick': true
16631         
16632     });
16633
16634 };
16635
16636 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16637     
16638      /**
16639      * @cfg {Number} startDay
16640      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16641      */
16642     startDay : 0,
16643     
16644     loadMask : false,
16645     
16646     header : false,
16647       
16648     getAutoCreate : function(){
16649         
16650         
16651         var fc_button = function(name, corner, style, content ) {
16652             return Roo.apply({},{
16653                 tag : 'span',
16654                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16655                          (corner.length ?
16656                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16657                             ''
16658                         ),
16659                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16660                 unselectable: 'on'
16661             });
16662         };
16663         
16664         var header = {};
16665         
16666         if(!this.header){
16667             header = {
16668                 tag : 'table',
16669                 cls : 'fc-header',
16670                 style : 'width:100%',
16671                 cn : [
16672                     {
16673                         tag: 'tr',
16674                         cn : [
16675                             {
16676                                 tag : 'td',
16677                                 cls : 'fc-header-left',
16678                                 cn : [
16679                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16680                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16681                                     { tag: 'span', cls: 'fc-header-space' },
16682                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16683
16684
16685                                 ]
16686                             },
16687
16688                             {
16689                                 tag : 'td',
16690                                 cls : 'fc-header-center',
16691                                 cn : [
16692                                     {
16693                                         tag: 'span',
16694                                         cls: 'fc-header-title',
16695                                         cn : {
16696                                             tag: 'H2',
16697                                             html : 'month / year'
16698                                         }
16699                                     }
16700
16701                                 ]
16702                             },
16703                             {
16704                                 tag : 'td',
16705                                 cls : 'fc-header-right',
16706                                 cn : [
16707                               /*      fc_button('month', 'left', '', 'month' ),
16708                                     fc_button('week', '', '', 'week' ),
16709                                     fc_button('day', 'right', '', 'day' )
16710                                 */    
16711
16712                                 ]
16713                             }
16714
16715                         ]
16716                     }
16717                 ]
16718             };
16719         }
16720         
16721         header = this.header;
16722         
16723        
16724         var cal_heads = function() {
16725             var ret = [];
16726             // fixme - handle this.
16727             
16728             for (var i =0; i < Date.dayNames.length; i++) {
16729                 var d = Date.dayNames[i];
16730                 ret.push({
16731                     tag: 'th',
16732                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16733                     html : d.substring(0,3)
16734                 });
16735                 
16736             }
16737             ret[0].cls += ' fc-first';
16738             ret[6].cls += ' fc-last';
16739             return ret;
16740         };
16741         var cal_cell = function(n) {
16742             return  {
16743                 tag: 'td',
16744                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16745                 cn : [
16746                     {
16747                         cn : [
16748                             {
16749                                 cls: 'fc-day-number',
16750                                 html: 'D'
16751                             },
16752                             {
16753                                 cls: 'fc-day-content',
16754                              
16755                                 cn : [
16756                                      {
16757                                         style: 'position: relative;' // height: 17px;
16758                                     }
16759                                 ]
16760                             }
16761                             
16762                             
16763                         ]
16764                     }
16765                 ]
16766                 
16767             }
16768         };
16769         var cal_rows = function() {
16770             
16771             var ret = [];
16772             for (var r = 0; r < 6; r++) {
16773                 var row= {
16774                     tag : 'tr',
16775                     cls : 'fc-week',
16776                     cn : []
16777                 };
16778                 
16779                 for (var i =0; i < Date.dayNames.length; i++) {
16780                     var d = Date.dayNames[i];
16781                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16782
16783                 }
16784                 row.cn[0].cls+=' fc-first';
16785                 row.cn[0].cn[0].style = 'min-height:90px';
16786                 row.cn[6].cls+=' fc-last';
16787                 ret.push(row);
16788                 
16789             }
16790             ret[0].cls += ' fc-first';
16791             ret[4].cls += ' fc-prev-last';
16792             ret[5].cls += ' fc-last';
16793             return ret;
16794             
16795         };
16796         
16797         var cal_table = {
16798             tag: 'table',
16799             cls: 'fc-border-separate',
16800             style : 'width:100%',
16801             cellspacing  : 0,
16802             cn : [
16803                 { 
16804                     tag: 'thead',
16805                     cn : [
16806                         { 
16807                             tag: 'tr',
16808                             cls : 'fc-first fc-last',
16809                             cn : cal_heads()
16810                         }
16811                     ]
16812                 },
16813                 { 
16814                     tag: 'tbody',
16815                     cn : cal_rows()
16816                 }
16817                   
16818             ]
16819         };
16820          
16821          var cfg = {
16822             cls : 'fc fc-ltr',
16823             cn : [
16824                 header,
16825                 {
16826                     cls : 'fc-content',
16827                     style : "position: relative;",
16828                     cn : [
16829                         {
16830                             cls : 'fc-view fc-view-month fc-grid',
16831                             style : 'position: relative',
16832                             unselectable : 'on',
16833                             cn : [
16834                                 {
16835                                     cls : 'fc-event-container',
16836                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16837                                 },
16838                                 cal_table
16839                             ]
16840                         }
16841                     ]
16842     
16843                 }
16844            ] 
16845             
16846         };
16847         
16848          
16849         
16850         return cfg;
16851     },
16852     
16853     
16854     initEvents : function()
16855     {
16856         if(!this.store){
16857             throw "can not find store for calendar";
16858         }
16859         
16860         var mark = {
16861             tag: "div",
16862             cls:"x-dlg-mask",
16863             style: "text-align:center",
16864             cn: [
16865                 {
16866                     tag: "div",
16867                     style: "background-color:white;width:50%;margin:250 auto",
16868                     cn: [
16869                         {
16870                             tag: "img",
16871                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16872                         },
16873                         {
16874                             tag: "span",
16875                             html: "Loading"
16876                         }
16877                         
16878                     ]
16879                 }
16880             ]
16881         };
16882         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16883         
16884         var size = this.el.select('.fc-content', true).first().getSize();
16885         this.maskEl.setSize(size.width, size.height);
16886         this.maskEl.enableDisplayMode("block");
16887         if(!this.loadMask){
16888             this.maskEl.hide();
16889         }
16890         
16891         this.store = Roo.factory(this.store, Roo.data);
16892         this.store.on('load', this.onLoad, this);
16893         this.store.on('beforeload', this.onBeforeLoad, this);
16894         
16895         this.resize();
16896         
16897         this.cells = this.el.select('.fc-day',true);
16898         //Roo.log(this.cells);
16899         this.textNodes = this.el.query('.fc-day-number');
16900         this.cells.addClassOnOver('fc-state-hover');
16901         
16902         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16903         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16904         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16905         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16906         
16907         this.on('monthchange', this.onMonthChange, this);
16908         
16909         this.update(new Date().clearTime());
16910     },
16911     
16912     resize : function() {
16913         var sz  = this.el.getSize();
16914         
16915         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16916         this.el.select('.fc-day-content div',true).setHeight(34);
16917     },
16918     
16919     
16920     // private
16921     showPrevMonth : function(e){
16922         this.update(this.activeDate.add("mo", -1));
16923     },
16924     showToday : function(e){
16925         this.update(new Date().clearTime());
16926     },
16927     // private
16928     showNextMonth : function(e){
16929         this.update(this.activeDate.add("mo", 1));
16930     },
16931
16932     // private
16933     showPrevYear : function(){
16934         this.update(this.activeDate.add("y", -1));
16935     },
16936
16937     // private
16938     showNextYear : function(){
16939         this.update(this.activeDate.add("y", 1));
16940     },
16941
16942     
16943    // private
16944     update : function(date)
16945     {
16946         var vd = this.activeDate;
16947         this.activeDate = date;
16948 //        if(vd && this.el){
16949 //            var t = date.getTime();
16950 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16951 //                Roo.log('using add remove');
16952 //                
16953 //                this.fireEvent('monthchange', this, date);
16954 //                
16955 //                this.cells.removeClass("fc-state-highlight");
16956 //                this.cells.each(function(c){
16957 //                   if(c.dateValue == t){
16958 //                       c.addClass("fc-state-highlight");
16959 //                       setTimeout(function(){
16960 //                            try{c.dom.firstChild.focus();}catch(e){}
16961 //                       }, 50);
16962 //                       return false;
16963 //                   }
16964 //                   return true;
16965 //                });
16966 //                return;
16967 //            }
16968 //        }
16969         
16970         var days = date.getDaysInMonth();
16971         
16972         var firstOfMonth = date.getFirstDateOfMonth();
16973         var startingPos = firstOfMonth.getDay()-this.startDay;
16974         
16975         if(startingPos < this.startDay){
16976             startingPos += 7;
16977         }
16978         
16979         var pm = date.add(Date.MONTH, -1);
16980         var prevStart = pm.getDaysInMonth()-startingPos;
16981 //        
16982         this.cells = this.el.select('.fc-day',true);
16983         this.textNodes = this.el.query('.fc-day-number');
16984         this.cells.addClassOnOver('fc-state-hover');
16985         
16986         var cells = this.cells.elements;
16987         var textEls = this.textNodes;
16988         
16989         Roo.each(cells, function(cell){
16990             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16991         });
16992         
16993         days += startingPos;
16994
16995         // convert everything to numbers so it's fast
16996         var day = 86400000;
16997         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16998         //Roo.log(d);
16999         //Roo.log(pm);
17000         //Roo.log(prevStart);
17001         
17002         var today = new Date().clearTime().getTime();
17003         var sel = date.clearTime().getTime();
17004         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17005         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17006         var ddMatch = this.disabledDatesRE;
17007         var ddText = this.disabledDatesText;
17008         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17009         var ddaysText = this.disabledDaysText;
17010         var format = this.format;
17011         
17012         var setCellClass = function(cal, cell){
17013             cell.row = 0;
17014             cell.events = [];
17015             cell.more = [];
17016             //Roo.log('set Cell Class');
17017             cell.title = "";
17018             var t = d.getTime();
17019             
17020             //Roo.log(d);
17021             
17022             cell.dateValue = t;
17023             if(t == today){
17024                 cell.className += " fc-today";
17025                 cell.className += " fc-state-highlight";
17026                 cell.title = cal.todayText;
17027             }
17028             if(t == sel){
17029                 // disable highlight in other month..
17030                 //cell.className += " fc-state-highlight";
17031                 
17032             }
17033             // disabling
17034             if(t < min) {
17035                 cell.className = " fc-state-disabled";
17036                 cell.title = cal.minText;
17037                 return;
17038             }
17039             if(t > max) {
17040                 cell.className = " fc-state-disabled";
17041                 cell.title = cal.maxText;
17042                 return;
17043             }
17044             if(ddays){
17045                 if(ddays.indexOf(d.getDay()) != -1){
17046                     cell.title = ddaysText;
17047                     cell.className = " fc-state-disabled";
17048                 }
17049             }
17050             if(ddMatch && format){
17051                 var fvalue = d.dateFormat(format);
17052                 if(ddMatch.test(fvalue)){
17053                     cell.title = ddText.replace("%0", fvalue);
17054                     cell.className = " fc-state-disabled";
17055                 }
17056             }
17057             
17058             if (!cell.initialClassName) {
17059                 cell.initialClassName = cell.dom.className;
17060             }
17061             
17062             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17063         };
17064
17065         var i = 0;
17066         
17067         for(; i < startingPos; i++) {
17068             textEls[i].innerHTML = (++prevStart);
17069             d.setDate(d.getDate()+1);
17070             
17071             cells[i].className = "fc-past fc-other-month";
17072             setCellClass(this, cells[i]);
17073         }
17074         
17075         var intDay = 0;
17076         
17077         for(; i < days; i++){
17078             intDay = i - startingPos + 1;
17079             textEls[i].innerHTML = (intDay);
17080             d.setDate(d.getDate()+1);
17081             
17082             cells[i].className = ''; // "x-date-active";
17083             setCellClass(this, cells[i]);
17084         }
17085         var extraDays = 0;
17086         
17087         for(; i < 42; i++) {
17088             textEls[i].innerHTML = (++extraDays);
17089             d.setDate(d.getDate()+1);
17090             
17091             cells[i].className = "fc-future fc-other-month";
17092             setCellClass(this, cells[i]);
17093         }
17094         
17095         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17096         
17097         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17098         
17099         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17100         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17101         
17102         if(totalRows != 6){
17103             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17104             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17105         }
17106         
17107         this.fireEvent('monthchange', this, date);
17108         
17109         
17110         /*
17111         if(!this.internalRender){
17112             var main = this.el.dom.firstChild;
17113             var w = main.offsetWidth;
17114             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17115             Roo.fly(main).setWidth(w);
17116             this.internalRender = true;
17117             // opera does not respect the auto grow header center column
17118             // then, after it gets a width opera refuses to recalculate
17119             // without a second pass
17120             if(Roo.isOpera && !this.secondPass){
17121                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17122                 this.secondPass = true;
17123                 this.update.defer(10, this, [date]);
17124             }
17125         }
17126         */
17127         
17128     },
17129     
17130     findCell : function(dt) {
17131         dt = dt.clearTime().getTime();
17132         var ret = false;
17133         this.cells.each(function(c){
17134             //Roo.log("check " +c.dateValue + '?=' + dt);
17135             if(c.dateValue == dt){
17136                 ret = c;
17137                 return false;
17138             }
17139             return true;
17140         });
17141         
17142         return ret;
17143     },
17144     
17145     findCells : function(ev) {
17146         var s = ev.start.clone().clearTime().getTime();
17147        // Roo.log(s);
17148         var e= ev.end.clone().clearTime().getTime();
17149        // Roo.log(e);
17150         var ret = [];
17151         this.cells.each(function(c){
17152              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17153             
17154             if(c.dateValue > e){
17155                 return ;
17156             }
17157             if(c.dateValue < s){
17158                 return ;
17159             }
17160             ret.push(c);
17161         });
17162         
17163         return ret;    
17164     },
17165     
17166 //    findBestRow: function(cells)
17167 //    {
17168 //        var ret = 0;
17169 //        
17170 //        for (var i =0 ; i < cells.length;i++) {
17171 //            ret  = Math.max(cells[i].rows || 0,ret);
17172 //        }
17173 //        return ret;
17174 //        
17175 //    },
17176     
17177     
17178     addItem : function(ev)
17179     {
17180         // look for vertical location slot in
17181         var cells = this.findCells(ev);
17182         
17183 //        ev.row = this.findBestRow(cells);
17184         
17185         // work out the location.
17186         
17187         var crow = false;
17188         var rows = [];
17189         for(var i =0; i < cells.length; i++) {
17190             
17191             cells[i].row = cells[0].row;
17192             
17193             if(i == 0){
17194                 cells[i].row = cells[i].row + 1;
17195             }
17196             
17197             if (!crow) {
17198                 crow = {
17199                     start : cells[i],
17200                     end :  cells[i]
17201                 };
17202                 continue;
17203             }
17204             if (crow.start.getY() == cells[i].getY()) {
17205                 // on same row.
17206                 crow.end = cells[i];
17207                 continue;
17208             }
17209             // different row.
17210             rows.push(crow);
17211             crow = {
17212                 start: cells[i],
17213                 end : cells[i]
17214             };
17215             
17216         }
17217         
17218         rows.push(crow);
17219         ev.els = [];
17220         ev.rows = rows;
17221         ev.cells = cells;
17222         
17223         cells[0].events.push(ev);
17224         
17225         this.calevents.push(ev);
17226     },
17227     
17228     clearEvents: function() {
17229         
17230         if(!this.calevents){
17231             return;
17232         }
17233         
17234         Roo.each(this.cells.elements, function(c){
17235             c.row = 0;
17236             c.events = [];
17237             c.more = [];
17238         });
17239         
17240         Roo.each(this.calevents, function(e) {
17241             Roo.each(e.els, function(el) {
17242                 el.un('mouseenter' ,this.onEventEnter, this);
17243                 el.un('mouseleave' ,this.onEventLeave, this);
17244                 el.remove();
17245             },this);
17246         },this);
17247         
17248         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17249             e.remove();
17250         });
17251         
17252     },
17253     
17254     renderEvents: function()
17255     {   
17256         var _this = this;
17257         
17258         this.cells.each(function(c) {
17259             
17260             if(c.row < 5){
17261                 return;
17262             }
17263             
17264             var ev = c.events;
17265             
17266             var r = 4;
17267             if(c.row != c.events.length){
17268                 r = 4 - (4 - (c.row - c.events.length));
17269             }
17270             
17271             c.events = ev.slice(0, r);
17272             c.more = ev.slice(r);
17273             
17274             if(c.more.length && c.more.length == 1){
17275                 c.events.push(c.more.pop());
17276             }
17277             
17278             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17279             
17280         });
17281             
17282         this.cells.each(function(c) {
17283             
17284             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17285             
17286             
17287             for (var e = 0; e < c.events.length; e++){
17288                 var ev = c.events[e];
17289                 var rows = ev.rows;
17290                 
17291                 for(var i = 0; i < rows.length; i++) {
17292                 
17293                     // how many rows should it span..
17294
17295                     var  cfg = {
17296                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17297                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17298
17299                         unselectable : "on",
17300                         cn : [
17301                             {
17302                                 cls: 'fc-event-inner',
17303                                 cn : [
17304     //                                {
17305     //                                  tag:'span',
17306     //                                  cls: 'fc-event-time',
17307     //                                  html : cells.length > 1 ? '' : ev.time
17308     //                                },
17309                                     {
17310                                       tag:'span',
17311                                       cls: 'fc-event-title',
17312                                       html : String.format('{0}', ev.title)
17313                                     }
17314
17315
17316                                 ]
17317                             },
17318                             {
17319                                 cls: 'ui-resizable-handle ui-resizable-e',
17320                                 html : '&nbsp;&nbsp;&nbsp'
17321                             }
17322
17323                         ]
17324                     };
17325
17326                     if (i == 0) {
17327                         cfg.cls += ' fc-event-start';
17328                     }
17329                     if ((i+1) == rows.length) {
17330                         cfg.cls += ' fc-event-end';
17331                     }
17332
17333                     var ctr = _this.el.select('.fc-event-container',true).first();
17334                     var cg = ctr.createChild(cfg);
17335
17336                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17337                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17338
17339                     var r = (c.more.length) ? 1 : 0;
17340                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17341                     cg.setWidth(ebox.right - sbox.x -2);
17342
17343                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17344                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17345                     cg.on('click', _this.onEventClick, _this, ev);
17346
17347                     ev.els.push(cg);
17348                     
17349                 }
17350                 
17351             }
17352             
17353             
17354             if(c.more.length){
17355                 var  cfg = {
17356                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17357                     style : 'position: absolute',
17358                     unselectable : "on",
17359                     cn : [
17360                         {
17361                             cls: 'fc-event-inner',
17362                             cn : [
17363                                 {
17364                                   tag:'span',
17365                                   cls: 'fc-event-title',
17366                                   html : 'More'
17367                                 }
17368
17369
17370                             ]
17371                         },
17372                         {
17373                             cls: 'ui-resizable-handle ui-resizable-e',
17374                             html : '&nbsp;&nbsp;&nbsp'
17375                         }
17376
17377                     ]
17378                 };
17379
17380                 var ctr = _this.el.select('.fc-event-container',true).first();
17381                 var cg = ctr.createChild(cfg);
17382
17383                 var sbox = c.select('.fc-day-content',true).first().getBox();
17384                 var ebox = c.select('.fc-day-content',true).first().getBox();
17385                 //Roo.log(cg);
17386                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17387                 cg.setWidth(ebox.right - sbox.x -2);
17388
17389                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17390                 
17391             }
17392             
17393         });
17394         
17395         
17396         
17397     },
17398     
17399     onEventEnter: function (e, el,event,d) {
17400         this.fireEvent('evententer', this, el, event);
17401     },
17402     
17403     onEventLeave: function (e, el,event,d) {
17404         this.fireEvent('eventleave', this, el, event);
17405     },
17406     
17407     onEventClick: function (e, el,event,d) {
17408         this.fireEvent('eventclick', this, el, event);
17409     },
17410     
17411     onMonthChange: function () {
17412         this.store.load();
17413     },
17414     
17415     onMoreEventClick: function(e, el, more)
17416     {
17417         var _this = this;
17418         
17419         this.calpopover.placement = 'right';
17420         this.calpopover.setTitle('More');
17421         
17422         this.calpopover.setContent('');
17423         
17424         var ctr = this.calpopover.el.select('.popover-content', true).first();
17425         
17426         Roo.each(more, function(m){
17427             var cfg = {
17428                 cls : 'fc-event-hori fc-event-draggable',
17429                 html : m.title
17430             };
17431             var cg = ctr.createChild(cfg);
17432             
17433             cg.on('click', _this.onEventClick, _this, m);
17434         });
17435         
17436         this.calpopover.show(el);
17437         
17438         
17439     },
17440     
17441     onLoad: function () 
17442     {   
17443         this.calevents = [];
17444         var cal = this;
17445         
17446         if(this.store.getCount() > 0){
17447             this.store.data.each(function(d){
17448                cal.addItem({
17449                     id : d.data.id,
17450                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17451                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17452                     time : d.data.start_time,
17453                     title : d.data.title,
17454                     description : d.data.description,
17455                     venue : d.data.venue
17456                 });
17457             });
17458         }
17459         
17460         this.renderEvents();
17461         
17462         if(this.calevents.length && this.loadMask){
17463             this.maskEl.hide();
17464         }
17465     },
17466     
17467     onBeforeLoad: function()
17468     {
17469         this.clearEvents();
17470         if(this.loadMask){
17471             this.maskEl.show();
17472         }
17473     }
17474 });
17475
17476  
17477  /*
17478  * - LGPL
17479  *
17480  * element
17481  * 
17482  */
17483
17484 /**
17485  * @class Roo.bootstrap.Popover
17486  * @extends Roo.bootstrap.Component
17487  * Bootstrap Popover class
17488  * @cfg {String} html contents of the popover   (or false to use children..)
17489  * @cfg {String} title of popover (or false to hide)
17490  * @cfg {String} placement how it is placed
17491  * @cfg {String} trigger click || hover (or false to trigger manually)
17492  * @cfg {String} over what (parent or false to trigger manually.)
17493  * @cfg {Number} delay - delay before showing
17494  
17495  * @constructor
17496  * Create a new Popover
17497  * @param {Object} config The config object
17498  */
17499
17500 Roo.bootstrap.Popover = function(config){
17501     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17502     
17503     this.addEvents({
17504         // raw events
17505          /**
17506          * @event show
17507          * After the popover show
17508          * 
17509          * @param {Roo.bootstrap.Popover} this
17510          */
17511         "show" : true,
17512         /**
17513          * @event hide
17514          * After the popover hide
17515          * 
17516          * @param {Roo.bootstrap.Popover} this
17517          */
17518         "hide" : true
17519     });
17520 };
17521
17522 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17523     
17524     title: 'Fill in a title',
17525     html: false,
17526     
17527     placement : 'right',
17528     trigger : 'hover', // hover
17529     
17530     delay : 0,
17531     
17532     over: 'parent',
17533     
17534     can_build_overlaid : false,
17535     
17536     getChildContainer : function()
17537     {
17538         return this.el.select('.popover-content',true).first();
17539     },
17540     
17541     getAutoCreate : function(){
17542          
17543         var cfg = {
17544            cls : 'popover roo-dynamic',
17545            style: 'display:block',
17546            cn : [
17547                 {
17548                     cls : 'arrow'
17549                 },
17550                 {
17551                     cls : 'popover-inner',
17552                     cn : [
17553                         {
17554                             tag: 'h3',
17555                             cls: 'popover-title',
17556                             html : this.title
17557                         },
17558                         {
17559                             cls : 'popover-content',
17560                             html : this.html
17561                         }
17562                     ]
17563                     
17564                 }
17565            ]
17566         };
17567         
17568         return cfg;
17569     },
17570     setTitle: function(str)
17571     {
17572         this.title = str;
17573         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17574     },
17575     setContent: function(str)
17576     {
17577         this.html = str;
17578         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17579     },
17580     // as it get's added to the bottom of the page.
17581     onRender : function(ct, position)
17582     {
17583         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17584         if(!this.el){
17585             var cfg = Roo.apply({},  this.getAutoCreate());
17586             cfg.id = Roo.id();
17587             
17588             if (this.cls) {
17589                 cfg.cls += ' ' + this.cls;
17590             }
17591             if (this.style) {
17592                 cfg.style = this.style;
17593             }
17594             //Roo.log("adding to ");
17595             this.el = Roo.get(document.body).createChild(cfg, position);
17596 //            Roo.log(this.el);
17597         }
17598         this.initEvents();
17599     },
17600     
17601     initEvents : function()
17602     {
17603         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17604         this.el.enableDisplayMode('block');
17605         this.el.hide();
17606         if (this.over === false) {
17607             return; 
17608         }
17609         if (this.triggers === false) {
17610             return;
17611         }
17612         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17613         var triggers = this.trigger ? this.trigger.split(' ') : [];
17614         Roo.each(triggers, function(trigger) {
17615         
17616             if (trigger == 'click') {
17617                 on_el.on('click', this.toggle, this);
17618             } else if (trigger != 'manual') {
17619                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17620                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17621       
17622                 on_el.on(eventIn  ,this.enter, this);
17623                 on_el.on(eventOut, this.leave, this);
17624             }
17625         }, this);
17626         
17627     },
17628     
17629     
17630     // private
17631     timeout : null,
17632     hoverState : null,
17633     
17634     toggle : function () {
17635         this.hoverState == 'in' ? this.leave() : this.enter();
17636     },
17637     
17638     enter : function () {
17639         
17640         clearTimeout(this.timeout);
17641     
17642         this.hoverState = 'in';
17643     
17644         if (!this.delay || !this.delay.show) {
17645             this.show();
17646             return;
17647         }
17648         var _t = this;
17649         this.timeout = setTimeout(function () {
17650             if (_t.hoverState == 'in') {
17651                 _t.show();
17652             }
17653         }, this.delay.show)
17654     },
17655     
17656     leave : function() {
17657         clearTimeout(this.timeout);
17658     
17659         this.hoverState = 'out';
17660     
17661         if (!this.delay || !this.delay.hide) {
17662             this.hide();
17663             return;
17664         }
17665         var _t = this;
17666         this.timeout = setTimeout(function () {
17667             if (_t.hoverState == 'out') {
17668                 _t.hide();
17669             }
17670         }, this.delay.hide)
17671     },
17672     
17673     show : function (on_el)
17674     {
17675         if (!on_el) {
17676             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17677         }
17678         
17679         // set content.
17680         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17681         if (this.html !== false) {
17682             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17683         }
17684         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17685         if (!this.title.length) {
17686             this.el.select('.popover-title',true).hide();
17687         }
17688         
17689         var placement = typeof this.placement == 'function' ?
17690             this.placement.call(this, this.el, on_el) :
17691             this.placement;
17692             
17693         var autoToken = /\s?auto?\s?/i;
17694         var autoPlace = autoToken.test(placement);
17695         if (autoPlace) {
17696             placement = placement.replace(autoToken, '') || 'top';
17697         }
17698         
17699         //this.el.detach()
17700         //this.el.setXY([0,0]);
17701         this.el.show();
17702         this.el.dom.style.display='block';
17703         this.el.addClass(placement);
17704         
17705         //this.el.appendTo(on_el);
17706         
17707         var p = this.getPosition();
17708         var box = this.el.getBox();
17709         
17710         if (autoPlace) {
17711             // fixme..
17712         }
17713         var align = Roo.bootstrap.Popover.alignment[placement];
17714         
17715 //        Roo.log(align);
17716         this.el.alignTo(on_el, align[0],align[1]);
17717         //var arrow = this.el.select('.arrow',true).first();
17718         //arrow.set(align[2], 
17719         
17720         this.el.addClass('in');
17721         
17722         
17723         if (this.el.hasClass('fade')) {
17724             // fade it?
17725         }
17726         
17727         this.hoverState = 'in';
17728         
17729         this.fireEvent('show', this);
17730         
17731     },
17732     hide : function()
17733     {
17734         this.el.setXY([0,0]);
17735         this.el.removeClass('in');
17736         this.el.hide();
17737         this.hoverState = null;
17738         
17739         this.fireEvent('hide', this);
17740     }
17741     
17742 });
17743
17744 Roo.bootstrap.Popover.alignment = {
17745     'left' : ['r-l', [-10,0], 'right'],
17746     'right' : ['l-r', [10,0], 'left'],
17747     'bottom' : ['t-b', [0,10], 'top'],
17748     'top' : [ 'b-t', [0,-10], 'bottom']
17749 };
17750
17751  /*
17752  * - LGPL
17753  *
17754  * Progress
17755  * 
17756  */
17757
17758 /**
17759  * @class Roo.bootstrap.Progress
17760  * @extends Roo.bootstrap.Component
17761  * Bootstrap Progress class
17762  * @cfg {Boolean} striped striped of the progress bar
17763  * @cfg {Boolean} active animated of the progress bar
17764  * 
17765  * 
17766  * @constructor
17767  * Create a new Progress
17768  * @param {Object} config The config object
17769  */
17770
17771 Roo.bootstrap.Progress = function(config){
17772     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17773 };
17774
17775 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17776     
17777     striped : false,
17778     active: false,
17779     
17780     getAutoCreate : function(){
17781         var cfg = {
17782             tag: 'div',
17783             cls: 'progress'
17784         };
17785         
17786         
17787         if(this.striped){
17788             cfg.cls += ' progress-striped';
17789         }
17790       
17791         if(this.active){
17792             cfg.cls += ' active';
17793         }
17794         
17795         
17796         return cfg;
17797     }
17798    
17799 });
17800
17801  
17802
17803  /*
17804  * - LGPL
17805  *
17806  * ProgressBar
17807  * 
17808  */
17809
17810 /**
17811  * @class Roo.bootstrap.ProgressBar
17812  * @extends Roo.bootstrap.Component
17813  * Bootstrap ProgressBar class
17814  * @cfg {Number} aria_valuenow aria-value now
17815  * @cfg {Number} aria_valuemin aria-value min
17816  * @cfg {Number} aria_valuemax aria-value max
17817  * @cfg {String} label label for the progress bar
17818  * @cfg {String} panel (success | info | warning | danger )
17819  * @cfg {String} role role of the progress bar
17820  * @cfg {String} sr_only text
17821  * 
17822  * 
17823  * @constructor
17824  * Create a new ProgressBar
17825  * @param {Object} config The config object
17826  */
17827
17828 Roo.bootstrap.ProgressBar = function(config){
17829     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17830 };
17831
17832 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17833     
17834     aria_valuenow : 0,
17835     aria_valuemin : 0,
17836     aria_valuemax : 100,
17837     label : false,
17838     panel : false,
17839     role : false,
17840     sr_only: false,
17841     
17842     getAutoCreate : function()
17843     {
17844         
17845         var cfg = {
17846             tag: 'div',
17847             cls: 'progress-bar',
17848             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17849         };
17850         
17851         if(this.sr_only){
17852             cfg.cn = {
17853                 tag: 'span',
17854                 cls: 'sr-only',
17855                 html: this.sr_only
17856             }
17857         }
17858         
17859         if(this.role){
17860             cfg.role = this.role;
17861         }
17862         
17863         if(this.aria_valuenow){
17864             cfg['aria-valuenow'] = this.aria_valuenow;
17865         }
17866         
17867         if(this.aria_valuemin){
17868             cfg['aria-valuemin'] = this.aria_valuemin;
17869         }
17870         
17871         if(this.aria_valuemax){
17872             cfg['aria-valuemax'] = this.aria_valuemax;
17873         }
17874         
17875         if(this.label && !this.sr_only){
17876             cfg.html = this.label;
17877         }
17878         
17879         if(this.panel){
17880             cfg.cls += ' progress-bar-' + this.panel;
17881         }
17882         
17883         return cfg;
17884     },
17885     
17886     update : function(aria_valuenow)
17887     {
17888         this.aria_valuenow = aria_valuenow;
17889         
17890         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17891     }
17892    
17893 });
17894
17895  
17896
17897  /*
17898  * - LGPL
17899  *
17900  * column
17901  * 
17902  */
17903
17904 /**
17905  * @class Roo.bootstrap.TabGroup
17906  * @extends Roo.bootstrap.Column
17907  * Bootstrap Column class
17908  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17909  * @cfg {Boolean} carousel true to make the group behave like a carousel
17910  * @cfg {Boolean} bullets show bullets for the panels
17911  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17912  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17913  * @cfg {Boolean} showarrow (true|false) show arrow default true
17914  * 
17915  * @constructor
17916  * Create a new TabGroup
17917  * @param {Object} config The config object
17918  */
17919
17920 Roo.bootstrap.TabGroup = function(config){
17921     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17922     if (!this.navId) {
17923         this.navId = Roo.id();
17924     }
17925     this.tabs = [];
17926     Roo.bootstrap.TabGroup.register(this);
17927     
17928 };
17929
17930 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17931     
17932     carousel : false,
17933     transition : false,
17934     bullets : 0,
17935     timer : 0,
17936     autoslide : false,
17937     slideFn : false,
17938     slideOnTouch : false,
17939     showarrow : true,
17940     
17941     getAutoCreate : function()
17942     {
17943         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17944         
17945         cfg.cls += ' tab-content';
17946         
17947         if (this.carousel) {
17948             cfg.cls += ' carousel slide';
17949             
17950             cfg.cn = [{
17951                cls : 'carousel-inner',
17952                cn : []
17953             }];
17954         
17955             if(this.bullets  && !Roo.isTouch){
17956                 
17957                 var bullets = {
17958                     cls : 'carousel-bullets',
17959                     cn : []
17960                 };
17961                
17962                 if(this.bullets_cls){
17963                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17964                 }
17965                 
17966                 bullets.cn.push({
17967                     cls : 'clear'
17968                 });
17969                 
17970                 cfg.cn[0].cn.push(bullets);
17971             }
17972             
17973             if(this.showarrow){
17974                 cfg.cn[0].cn.push({
17975                     tag : 'div',
17976                     class : 'carousel-arrow',
17977                     cn : [
17978                         {
17979                             tag : 'div',
17980                             class : 'carousel-prev',
17981                             cn : [
17982                                 {
17983                                     tag : 'i',
17984                                     class : 'fa fa-chevron-left'
17985                                 }
17986                             ]
17987                         },
17988                         {
17989                             tag : 'div',
17990                             class : 'carousel-next',
17991                             cn : [
17992                                 {
17993                                     tag : 'i',
17994                                     class : 'fa fa-chevron-right'
17995                                 }
17996                             ]
17997                         }
17998                     ]
17999                 });
18000             }
18001             
18002         }
18003         
18004         return cfg;
18005     },
18006     
18007     initEvents:  function()
18008     {
18009 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18010 //            this.el.on("touchstart", this.onTouchStart, this);
18011 //        }
18012         
18013         if(this.autoslide){
18014             var _this = this;
18015             
18016             this.slideFn = window.setInterval(function() {
18017                 _this.showPanelNext();
18018             }, this.timer);
18019         }
18020         
18021         if(this.showarrow){
18022             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18023             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18024         }
18025         
18026         
18027     },
18028     
18029 //    onTouchStart : function(e, el, o)
18030 //    {
18031 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18032 //            return;
18033 //        }
18034 //        
18035 //        this.showPanelNext();
18036 //    },
18037     
18038     
18039     getChildContainer : function()
18040     {
18041         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18042     },
18043     
18044     /**
18045     * register a Navigation item
18046     * @param {Roo.bootstrap.NavItem} the navitem to add
18047     */
18048     register : function(item)
18049     {
18050         this.tabs.push( item);
18051         item.navId = this.navId; // not really needed..
18052         this.addBullet();
18053     
18054     },
18055     
18056     getActivePanel : function()
18057     {
18058         var r = false;
18059         Roo.each(this.tabs, function(t) {
18060             if (t.active) {
18061                 r = t;
18062                 return false;
18063             }
18064             return null;
18065         });
18066         return r;
18067         
18068     },
18069     getPanelByName : function(n)
18070     {
18071         var r = false;
18072         Roo.each(this.tabs, function(t) {
18073             if (t.tabId == n) {
18074                 r = t;
18075                 return false;
18076             }
18077             return null;
18078         });
18079         return r;
18080     },
18081     indexOfPanel : function(p)
18082     {
18083         var r = false;
18084         Roo.each(this.tabs, function(t,i) {
18085             if (t.tabId == p.tabId) {
18086                 r = i;
18087                 return false;
18088             }
18089             return null;
18090         });
18091         return r;
18092     },
18093     /**
18094      * show a specific panel
18095      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18096      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18097      */
18098     showPanel : function (pan)
18099     {
18100         if(this.transition || typeof(pan) == 'undefined'){
18101             Roo.log("waiting for the transitionend");
18102             return;
18103         }
18104         
18105         if (typeof(pan) == 'number') {
18106             pan = this.tabs[pan];
18107         }
18108         
18109         if (typeof(pan) == 'string') {
18110             pan = this.getPanelByName(pan);
18111         }
18112         
18113         var cur = this.getActivePanel();
18114         
18115         if(!pan || !cur){
18116             Roo.log('pan or acitve pan is undefined');
18117             return false;
18118         }
18119         
18120         if (pan.tabId == this.getActivePanel().tabId) {
18121             return true;
18122         }
18123         
18124         if (false === cur.fireEvent('beforedeactivate')) {
18125             return false;
18126         }
18127         
18128         if(this.bullets > 0 && !Roo.isTouch){
18129             this.setActiveBullet(this.indexOfPanel(pan));
18130         }
18131         
18132         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18133             
18134             this.transition = true;
18135             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18136             var lr = dir == 'next' ? 'left' : 'right';
18137             pan.el.addClass(dir); // or prev
18138             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18139             cur.el.addClass(lr); // or right
18140             pan.el.addClass(lr);
18141             
18142             var _this = this;
18143             cur.el.on('transitionend', function() {
18144                 Roo.log("trans end?");
18145                 
18146                 pan.el.removeClass([lr,dir]);
18147                 pan.setActive(true);
18148                 
18149                 cur.el.removeClass([lr]);
18150                 cur.setActive(false);
18151                 
18152                 _this.transition = false;
18153                 
18154             }, this, { single:  true } );
18155             
18156             return true;
18157         }
18158         
18159         cur.setActive(false);
18160         pan.setActive(true);
18161         
18162         return true;
18163         
18164     },
18165     showPanelNext : function()
18166     {
18167         var i = this.indexOfPanel(this.getActivePanel());
18168         
18169         if (i >= this.tabs.length - 1 && !this.autoslide) {
18170             return;
18171         }
18172         
18173         if (i >= this.tabs.length - 1 && this.autoslide) {
18174             i = -1;
18175         }
18176         
18177         this.showPanel(this.tabs[i+1]);
18178     },
18179     
18180     showPanelPrev : function()
18181     {
18182         var i = this.indexOfPanel(this.getActivePanel());
18183         
18184         if (i  < 1 && !this.autoslide) {
18185             return;
18186         }
18187         
18188         if (i < 1 && this.autoslide) {
18189             i = this.tabs.length;
18190         }
18191         
18192         this.showPanel(this.tabs[i-1]);
18193     },
18194     
18195     
18196     addBullet: function()
18197     {
18198         if(!this.bullets || Roo.isTouch){
18199             return;
18200         }
18201         var ctr = this.el.select('.carousel-bullets',true).first();
18202         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18203         var bullet = ctr.createChild({
18204             cls : 'bullet bullet-' + i
18205         },ctr.dom.lastChild);
18206         
18207         
18208         var _this = this;
18209         
18210         bullet.on('click', (function(e, el, o, ii, t){
18211
18212             e.preventDefault();
18213
18214             this.showPanel(ii);
18215
18216             if(this.autoslide && this.slideFn){
18217                 clearInterval(this.slideFn);
18218                 this.slideFn = window.setInterval(function() {
18219                     _this.showPanelNext();
18220                 }, this.timer);
18221             }
18222
18223         }).createDelegate(this, [i, bullet], true));
18224                 
18225         
18226     },
18227      
18228     setActiveBullet : function(i)
18229     {
18230         if(Roo.isTouch){
18231             return;
18232         }
18233         
18234         Roo.each(this.el.select('.bullet', true).elements, function(el){
18235             el.removeClass('selected');
18236         });
18237
18238         var bullet = this.el.select('.bullet-' + i, true).first();
18239         
18240         if(!bullet){
18241             return;
18242         }
18243         
18244         bullet.addClass('selected');
18245     }
18246     
18247     
18248   
18249 });
18250
18251  
18252
18253  
18254  
18255 Roo.apply(Roo.bootstrap.TabGroup, {
18256     
18257     groups: {},
18258      /**
18259     * register a Navigation Group
18260     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18261     */
18262     register : function(navgrp)
18263     {
18264         this.groups[navgrp.navId] = navgrp;
18265         
18266     },
18267     /**
18268     * fetch a Navigation Group based on the navigation ID
18269     * if one does not exist , it will get created.
18270     * @param {string} the navgroup to add
18271     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18272     */
18273     get: function(navId) {
18274         if (typeof(this.groups[navId]) == 'undefined') {
18275             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18276         }
18277         return this.groups[navId] ;
18278     }
18279     
18280     
18281     
18282 });
18283
18284  /*
18285  * - LGPL
18286  *
18287  * TabPanel
18288  * 
18289  */
18290
18291 /**
18292  * @class Roo.bootstrap.TabPanel
18293  * @extends Roo.bootstrap.Component
18294  * Bootstrap TabPanel class
18295  * @cfg {Boolean} active panel active
18296  * @cfg {String} html panel content
18297  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18298  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18299  * @cfg {String} href click to link..
18300  * 
18301  * 
18302  * @constructor
18303  * Create a new TabPanel
18304  * @param {Object} config The config object
18305  */
18306
18307 Roo.bootstrap.TabPanel = function(config){
18308     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18309     this.addEvents({
18310         /**
18311              * @event changed
18312              * Fires when the active status changes
18313              * @param {Roo.bootstrap.TabPanel} this
18314              * @param {Boolean} state the new state
18315             
18316          */
18317         'changed': true,
18318         /**
18319              * @event beforedeactivate
18320              * Fires before a tab is de-activated - can be used to do validation on a form.
18321              * @param {Roo.bootstrap.TabPanel} this
18322              * @return {Boolean} false if there is an error
18323             
18324          */
18325         'beforedeactivate': true
18326      });
18327     
18328     this.tabId = this.tabId || Roo.id();
18329   
18330 };
18331
18332 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18333     
18334     active: false,
18335     html: false,
18336     tabId: false,
18337     navId : false,
18338     href : '',
18339     
18340     getAutoCreate : function(){
18341         var cfg = {
18342             tag: 'div',
18343             // item is needed for carousel - not sure if it has any effect otherwise
18344             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18345             html: this.html || ''
18346         };
18347         
18348         if(this.active){
18349             cfg.cls += ' active';
18350         }
18351         
18352         if(this.tabId){
18353             cfg.tabId = this.tabId;
18354         }
18355         
18356         
18357         return cfg;
18358     },
18359     
18360     initEvents:  function()
18361     {
18362         var p = this.parent();
18363         
18364         this.navId = this.navId || p.navId;
18365         
18366         if (typeof(this.navId) != 'undefined') {
18367             // not really needed.. but just in case.. parent should be a NavGroup.
18368             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18369             
18370             tg.register(this);
18371             
18372             var i = tg.tabs.length - 1;
18373             
18374             if(this.active && tg.bullets > 0 && i < tg.bullets){
18375                 tg.setActiveBullet(i);
18376             }
18377         }
18378         
18379         this.el.on('click', this.onClick, this);
18380         
18381         if(Roo.isTouch){
18382             this.el.on("touchstart", this.onTouchStart, this);
18383             this.el.on("touchmove", this.onTouchMove, this);
18384             this.el.on("touchend", this.onTouchEnd, this);
18385         }
18386         
18387     },
18388     
18389     onRender : function(ct, position)
18390     {
18391         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18392     },
18393     
18394     setActive : function(state)
18395     {
18396         Roo.log("panel - set active " + this.tabId + "=" + state);
18397         
18398         this.active = state;
18399         if (!state) {
18400             this.el.removeClass('active');
18401             
18402         } else  if (!this.el.hasClass('active')) {
18403             this.el.addClass('active');
18404         }
18405         
18406         this.fireEvent('changed', this, state);
18407     },
18408     
18409     onClick : function(e)
18410     {
18411         e.preventDefault();
18412         
18413         if(!this.href.length){
18414             return;
18415         }
18416         
18417         window.location.href = this.href;
18418     },
18419     
18420     startX : 0,
18421     startY : 0,
18422     endX : 0,
18423     endY : 0,
18424     swiping : false,
18425     
18426     onTouchStart : function(e)
18427     {
18428         this.swiping = false;
18429         
18430         this.startX = e.browserEvent.touches[0].clientX;
18431         this.startY = e.browserEvent.touches[0].clientY;
18432     },
18433     
18434     onTouchMove : function(e)
18435     {
18436         this.swiping = true;
18437         
18438         this.endX = e.browserEvent.touches[0].clientX;
18439         this.endY = e.browserEvent.touches[0].clientY;
18440     },
18441     
18442     onTouchEnd : function(e)
18443     {
18444         if(!this.swiping){
18445             this.onClick(e);
18446             return;
18447         }
18448         
18449         var tabGroup = this.parent();
18450         
18451         if(this.endX > this.startX){ // swiping right
18452             tabGroup.showPanelPrev();
18453             return;
18454         }
18455         
18456         if(this.startX > this.endX){ // swiping left
18457             tabGroup.showPanelNext();
18458             return;
18459         }
18460     }
18461     
18462     
18463 });
18464  
18465
18466  
18467
18468  /*
18469  * - LGPL
18470  *
18471  * DateField
18472  * 
18473  */
18474
18475 /**
18476  * @class Roo.bootstrap.DateField
18477  * @extends Roo.bootstrap.Input
18478  * Bootstrap DateField class
18479  * @cfg {Number} weekStart default 0
18480  * @cfg {String} viewMode default empty, (months|years)
18481  * @cfg {String} minViewMode default empty, (months|years)
18482  * @cfg {Number} startDate default -Infinity
18483  * @cfg {Number} endDate default Infinity
18484  * @cfg {Boolean} todayHighlight default false
18485  * @cfg {Boolean} todayBtn default false
18486  * @cfg {Boolean} calendarWeeks default false
18487  * @cfg {Object} daysOfWeekDisabled default empty
18488  * @cfg {Boolean} singleMode default false (true | false)
18489  * 
18490  * @cfg {Boolean} keyboardNavigation default true
18491  * @cfg {String} language default en
18492  * 
18493  * @constructor
18494  * Create a new DateField
18495  * @param {Object} config The config object
18496  */
18497
18498 Roo.bootstrap.DateField = function(config){
18499     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18500      this.addEvents({
18501             /**
18502              * @event show
18503              * Fires when this field show.
18504              * @param {Roo.bootstrap.DateField} this
18505              * @param {Mixed} date The date value
18506              */
18507             show : true,
18508             /**
18509              * @event show
18510              * Fires when this field hide.
18511              * @param {Roo.bootstrap.DateField} this
18512              * @param {Mixed} date The date value
18513              */
18514             hide : true,
18515             /**
18516              * @event select
18517              * Fires when select a date.
18518              * @param {Roo.bootstrap.DateField} this
18519              * @param {Mixed} date The date value
18520              */
18521             select : true,
18522             /**
18523              * @event beforeselect
18524              * Fires when before select a date.
18525              * @param {Roo.bootstrap.DateField} this
18526              * @param {Mixed} date The date value
18527              */
18528             beforeselect : true
18529         });
18530 };
18531
18532 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18533     
18534     /**
18535      * @cfg {String} format
18536      * The default date format string which can be overriden for localization support.  The format must be
18537      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18538      */
18539     format : "m/d/y",
18540     /**
18541      * @cfg {String} altFormats
18542      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18543      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18544      */
18545     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18546     
18547     weekStart : 0,
18548     
18549     viewMode : '',
18550     
18551     minViewMode : '',
18552     
18553     todayHighlight : false,
18554     
18555     todayBtn: false,
18556     
18557     language: 'en',
18558     
18559     keyboardNavigation: true,
18560     
18561     calendarWeeks: false,
18562     
18563     startDate: -Infinity,
18564     
18565     endDate: Infinity,
18566     
18567     daysOfWeekDisabled: [],
18568     
18569     _events: [],
18570     
18571     singleMode : false,
18572     
18573     UTCDate: function()
18574     {
18575         return new Date(Date.UTC.apply(Date, arguments));
18576     },
18577     
18578     UTCToday: function()
18579     {
18580         var today = new Date();
18581         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18582     },
18583     
18584     getDate: function() {
18585             var d = this.getUTCDate();
18586             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18587     },
18588     
18589     getUTCDate: function() {
18590             return this.date;
18591     },
18592     
18593     setDate: function(d) {
18594             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18595     },
18596     
18597     setUTCDate: function(d) {
18598             this.date = d;
18599             this.setValue(this.formatDate(this.date));
18600     },
18601         
18602     onRender: function(ct, position)
18603     {
18604         
18605         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18606         
18607         this.language = this.language || 'en';
18608         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18609         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18610         
18611         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18612         this.format = this.format || 'm/d/y';
18613         this.isInline = false;
18614         this.isInput = true;
18615         this.component = this.el.select('.add-on', true).first() || false;
18616         this.component = (this.component && this.component.length === 0) ? false : this.component;
18617         this.hasInput = this.component && this.inputEl().length;
18618         
18619         if (typeof(this.minViewMode === 'string')) {
18620             switch (this.minViewMode) {
18621                 case 'months':
18622                     this.minViewMode = 1;
18623                     break;
18624                 case 'years':
18625                     this.minViewMode = 2;
18626                     break;
18627                 default:
18628                     this.minViewMode = 0;
18629                     break;
18630             }
18631         }
18632         
18633         if (typeof(this.viewMode === 'string')) {
18634             switch (this.viewMode) {
18635                 case 'months':
18636                     this.viewMode = 1;
18637                     break;
18638                 case 'years':
18639                     this.viewMode = 2;
18640                     break;
18641                 default:
18642                     this.viewMode = 0;
18643                     break;
18644             }
18645         }
18646                 
18647         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18648         
18649 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18650         
18651         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18652         
18653         this.picker().on('mousedown', this.onMousedown, this);
18654         this.picker().on('click', this.onClick, this);
18655         
18656         this.picker().addClass('datepicker-dropdown');
18657         
18658         this.startViewMode = this.viewMode;
18659         
18660         if(this.singleMode){
18661             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18662                 v.setVisibilityMode(Roo.Element.DISPLAY);
18663                 v.hide();
18664             });
18665             
18666             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18667                 v.setStyle('width', '189px');
18668             });
18669         }
18670         
18671         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18672             if(!this.calendarWeeks){
18673                 v.remove();
18674                 return;
18675             }
18676             
18677             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18678             v.attr('colspan', function(i, val){
18679                 return parseInt(val) + 1;
18680             });
18681         });
18682                         
18683         
18684         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18685         
18686         this.setStartDate(this.startDate);
18687         this.setEndDate(this.endDate);
18688         
18689         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18690         
18691         this.fillDow();
18692         this.fillMonths();
18693         this.update();
18694         this.showMode();
18695         
18696         if(this.isInline) {
18697             this.showPopup();
18698         }
18699     },
18700     
18701     picker : function()
18702     {
18703         return this.pickerEl;
18704 //        return this.el.select('.datepicker', true).first();
18705     },
18706     
18707     fillDow: function()
18708     {
18709         var dowCnt = this.weekStart;
18710         
18711         var dow = {
18712             tag: 'tr',
18713             cn: [
18714                 
18715             ]
18716         };
18717         
18718         if(this.calendarWeeks){
18719             dow.cn.push({
18720                 tag: 'th',
18721                 cls: 'cw',
18722                 html: '&nbsp;'
18723             })
18724         }
18725         
18726         while (dowCnt < this.weekStart + 7) {
18727             dow.cn.push({
18728                 tag: 'th',
18729                 cls: 'dow',
18730                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18731             });
18732         }
18733         
18734         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18735     },
18736     
18737     fillMonths: function()
18738     {    
18739         var i = 0;
18740         var months = this.picker().select('>.datepicker-months td', true).first();
18741         
18742         months.dom.innerHTML = '';
18743         
18744         while (i < 12) {
18745             var month = {
18746                 tag: 'span',
18747                 cls: 'month',
18748                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18749             };
18750             
18751             months.createChild(month);
18752         }
18753         
18754     },
18755     
18756     update: function()
18757     {
18758         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;
18759         
18760         if (this.date < this.startDate) {
18761             this.viewDate = new Date(this.startDate);
18762         } else if (this.date > this.endDate) {
18763             this.viewDate = new Date(this.endDate);
18764         } else {
18765             this.viewDate = new Date(this.date);
18766         }
18767         
18768         this.fill();
18769     },
18770     
18771     fill: function() 
18772     {
18773         var d = new Date(this.viewDate),
18774                 year = d.getUTCFullYear(),
18775                 month = d.getUTCMonth(),
18776                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18777                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18778                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18779                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18780                 currentDate = this.date && this.date.valueOf(),
18781                 today = this.UTCToday();
18782         
18783         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18784         
18785 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18786         
18787 //        this.picker.select('>tfoot th.today').
18788 //                                              .text(dates[this.language].today)
18789 //                                              .toggle(this.todayBtn !== false);
18790     
18791         this.updateNavArrows();
18792         this.fillMonths();
18793                                                 
18794         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18795         
18796         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18797          
18798         prevMonth.setUTCDate(day);
18799         
18800         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18801         
18802         var nextMonth = new Date(prevMonth);
18803         
18804         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18805         
18806         nextMonth = nextMonth.valueOf();
18807         
18808         var fillMonths = false;
18809         
18810         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18811         
18812         while(prevMonth.valueOf() <= nextMonth) {
18813             var clsName = '';
18814             
18815             if (prevMonth.getUTCDay() === this.weekStart) {
18816                 if(fillMonths){
18817                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18818                 }
18819                     
18820                 fillMonths = {
18821                     tag: 'tr',
18822                     cn: []
18823                 };
18824                 
18825                 if(this.calendarWeeks){
18826                     // ISO 8601: First week contains first thursday.
18827                     // ISO also states week starts on Monday, but we can be more abstract here.
18828                     var
18829                     // Start of current week: based on weekstart/current date
18830                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18831                     // Thursday of this week
18832                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18833                     // First Thursday of year, year from thursday
18834                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18835                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18836                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18837                     
18838                     fillMonths.cn.push({
18839                         tag: 'td',
18840                         cls: 'cw',
18841                         html: calWeek
18842                     });
18843                 }
18844             }
18845             
18846             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18847                 clsName += ' old';
18848             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18849                 clsName += ' new';
18850             }
18851             if (this.todayHighlight &&
18852                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18853                 prevMonth.getUTCMonth() == today.getMonth() &&
18854                 prevMonth.getUTCDate() == today.getDate()) {
18855                 clsName += ' today';
18856             }
18857             
18858             if (currentDate && prevMonth.valueOf() === currentDate) {
18859                 clsName += ' active';
18860             }
18861             
18862             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18863                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18864                     clsName += ' disabled';
18865             }
18866             
18867             fillMonths.cn.push({
18868                 tag: 'td',
18869                 cls: 'day ' + clsName,
18870                 html: prevMonth.getDate()
18871             });
18872             
18873             prevMonth.setDate(prevMonth.getDate()+1);
18874         }
18875           
18876         var currentYear = this.date && this.date.getUTCFullYear();
18877         var currentMonth = this.date && this.date.getUTCMonth();
18878         
18879         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18880         
18881         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18882             v.removeClass('active');
18883             
18884             if(currentYear === year && k === currentMonth){
18885                 v.addClass('active');
18886             }
18887             
18888             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18889                 v.addClass('disabled');
18890             }
18891             
18892         });
18893         
18894         
18895         year = parseInt(year/10, 10) * 10;
18896         
18897         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18898         
18899         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18900         
18901         year -= 1;
18902         for (var i = -1; i < 11; i++) {
18903             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18904                 tag: 'span',
18905                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18906                 html: year
18907             });
18908             
18909             year += 1;
18910         }
18911     },
18912     
18913     showMode: function(dir) 
18914     {
18915         if (dir) {
18916             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18917         }
18918         
18919         Roo.each(this.picker().select('>div',true).elements, function(v){
18920             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18921             v.hide();
18922         });
18923         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18924     },
18925     
18926     place: function()
18927     {
18928         if(this.isInline) {
18929             return;
18930         }
18931         
18932         this.picker().removeClass(['bottom', 'top']);
18933         
18934         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18935             /*
18936              * place to the top of element!
18937              *
18938              */
18939             
18940             this.picker().addClass('top');
18941             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18942             
18943             return;
18944         }
18945         
18946         this.picker().addClass('bottom');
18947         
18948         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18949     },
18950     
18951     parseDate : function(value)
18952     {
18953         if(!value || value instanceof Date){
18954             return value;
18955         }
18956         var v = Date.parseDate(value, this.format);
18957         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18958             v = Date.parseDate(value, 'Y-m-d');
18959         }
18960         if(!v && this.altFormats){
18961             if(!this.altFormatsArray){
18962                 this.altFormatsArray = this.altFormats.split("|");
18963             }
18964             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18965                 v = Date.parseDate(value, this.altFormatsArray[i]);
18966             }
18967         }
18968         return v;
18969     },
18970     
18971     formatDate : function(date, fmt)
18972     {   
18973         return (!date || !(date instanceof Date)) ?
18974         date : date.dateFormat(fmt || this.format);
18975     },
18976     
18977     onFocus : function()
18978     {
18979         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18980         this.showPopup();
18981     },
18982     
18983     onBlur : function()
18984     {
18985         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18986         
18987         var d = this.inputEl().getValue();
18988         
18989         this.setValue(d);
18990                 
18991         this.hidePopup();
18992     },
18993     
18994     showPopup : function()
18995     {
18996         this.picker().show();
18997         this.update();
18998         this.place();
18999         
19000         this.fireEvent('showpopup', this, this.date);
19001     },
19002     
19003     hidePopup : function()
19004     {
19005         if(this.isInline) {
19006             return;
19007         }
19008         this.picker().hide();
19009         this.viewMode = this.startViewMode;
19010         this.showMode();
19011         
19012         this.fireEvent('hidepopup', this, this.date);
19013         
19014     },
19015     
19016     onMousedown: function(e)
19017     {
19018         e.stopPropagation();
19019         e.preventDefault();
19020     },
19021     
19022     keyup: function(e)
19023     {
19024         Roo.bootstrap.DateField.superclass.keyup.call(this);
19025         this.update();
19026     },
19027
19028     setValue: function(v)
19029     {
19030         if(this.fireEvent('beforeselect', this, v) !== false){
19031             var d = new Date(this.parseDate(v) ).clearTime();
19032         
19033             if(isNaN(d.getTime())){
19034                 this.date = this.viewDate = '';
19035                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19036                 return;
19037             }
19038
19039             v = this.formatDate(d);
19040
19041             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19042
19043             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19044
19045             this.update();
19046
19047             this.fireEvent('select', this, this.date);
19048         }
19049     },
19050     
19051     getValue: function()
19052     {
19053         return this.formatDate(this.date);
19054     },
19055     
19056     fireKey: function(e)
19057     {
19058         if (!this.picker().isVisible()){
19059             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19060                 this.showPopup();
19061             }
19062             return;
19063         }
19064         
19065         var dateChanged = false,
19066         dir, day, month,
19067         newDate, newViewDate;
19068         
19069         switch(e.keyCode){
19070             case 27: // escape
19071                 this.hidePopup();
19072                 e.preventDefault();
19073                 break;
19074             case 37: // left
19075             case 39: // right
19076                 if (!this.keyboardNavigation) {
19077                     break;
19078                 }
19079                 dir = e.keyCode == 37 ? -1 : 1;
19080                 
19081                 if (e.ctrlKey){
19082                     newDate = this.moveYear(this.date, dir);
19083                     newViewDate = this.moveYear(this.viewDate, dir);
19084                 } else if (e.shiftKey){
19085                     newDate = this.moveMonth(this.date, dir);
19086                     newViewDate = this.moveMonth(this.viewDate, dir);
19087                 } else {
19088                     newDate = new Date(this.date);
19089                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19090                     newViewDate = new Date(this.viewDate);
19091                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19092                 }
19093                 if (this.dateWithinRange(newDate)){
19094                     this.date = newDate;
19095                     this.viewDate = newViewDate;
19096                     this.setValue(this.formatDate(this.date));
19097 //                    this.update();
19098                     e.preventDefault();
19099                     dateChanged = true;
19100                 }
19101                 break;
19102             case 38: // up
19103             case 40: // down
19104                 if (!this.keyboardNavigation) {
19105                     break;
19106                 }
19107                 dir = e.keyCode == 38 ? -1 : 1;
19108                 if (e.ctrlKey){
19109                     newDate = this.moveYear(this.date, dir);
19110                     newViewDate = this.moveYear(this.viewDate, dir);
19111                 } else if (e.shiftKey){
19112                     newDate = this.moveMonth(this.date, dir);
19113                     newViewDate = this.moveMonth(this.viewDate, dir);
19114                 } else {
19115                     newDate = new Date(this.date);
19116                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19117                     newViewDate = new Date(this.viewDate);
19118                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19119                 }
19120                 if (this.dateWithinRange(newDate)){
19121                     this.date = newDate;
19122                     this.viewDate = newViewDate;
19123                     this.setValue(this.formatDate(this.date));
19124 //                    this.update();
19125                     e.preventDefault();
19126                     dateChanged = true;
19127                 }
19128                 break;
19129             case 13: // enter
19130                 this.setValue(this.formatDate(this.date));
19131                 this.hidePopup();
19132                 e.preventDefault();
19133                 break;
19134             case 9: // tab
19135                 this.setValue(this.formatDate(this.date));
19136                 this.hidePopup();
19137                 break;
19138             case 16: // shift
19139             case 17: // ctrl
19140             case 18: // alt
19141                 break;
19142             default :
19143                 this.hide();
19144                 
19145         }
19146     },
19147     
19148     
19149     onClick: function(e) 
19150     {
19151         e.stopPropagation();
19152         e.preventDefault();
19153         
19154         var target = e.getTarget();
19155         
19156         if(target.nodeName.toLowerCase() === 'i'){
19157             target = Roo.get(target).dom.parentNode;
19158         }
19159         
19160         var nodeName = target.nodeName;
19161         var className = target.className;
19162         var html = target.innerHTML;
19163         //Roo.log(nodeName);
19164         
19165         switch(nodeName.toLowerCase()) {
19166             case 'th':
19167                 switch(className) {
19168                     case 'switch':
19169                         this.showMode(1);
19170                         break;
19171                     case 'prev':
19172                     case 'next':
19173                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19174                         switch(this.viewMode){
19175                                 case 0:
19176                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19177                                         break;
19178                                 case 1:
19179                                 case 2:
19180                                         this.viewDate = this.moveYear(this.viewDate, dir);
19181                                         break;
19182                         }
19183                         this.fill();
19184                         break;
19185                     case 'today':
19186                         var date = new Date();
19187                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19188 //                        this.fill()
19189                         this.setValue(this.formatDate(this.date));
19190                         
19191                         this.hidePopup();
19192                         break;
19193                 }
19194                 break;
19195             case 'span':
19196                 if (className.indexOf('disabled') < 0) {
19197                     this.viewDate.setUTCDate(1);
19198                     if (className.indexOf('month') > -1) {
19199                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19200                     } else {
19201                         var year = parseInt(html, 10) || 0;
19202                         this.viewDate.setUTCFullYear(year);
19203                         
19204                     }
19205                     
19206                     if(this.singleMode){
19207                         this.setValue(this.formatDate(this.viewDate));
19208                         this.hidePopup();
19209                         return;
19210                     }
19211                     
19212                     this.showMode(-1);
19213                     this.fill();
19214                 }
19215                 break;
19216                 
19217             case 'td':
19218                 //Roo.log(className);
19219                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19220                     var day = parseInt(html, 10) || 1;
19221                     var year = this.viewDate.getUTCFullYear(),
19222                         month = this.viewDate.getUTCMonth();
19223
19224                     if (className.indexOf('old') > -1) {
19225                         if(month === 0 ){
19226                             month = 11;
19227                             year -= 1;
19228                         }else{
19229                             month -= 1;
19230                         }
19231                     } else if (className.indexOf('new') > -1) {
19232                         if (month == 11) {
19233                             month = 0;
19234                             year += 1;
19235                         } else {
19236                             month += 1;
19237                         }
19238                     }
19239                     //Roo.log([year,month,day]);
19240                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19241                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19242 //                    this.fill();
19243                     //Roo.log(this.formatDate(this.date));
19244                     this.setValue(this.formatDate(this.date));
19245                     this.hidePopup();
19246                 }
19247                 break;
19248         }
19249     },
19250     
19251     setStartDate: function(startDate)
19252     {
19253         this.startDate = startDate || -Infinity;
19254         if (this.startDate !== -Infinity) {
19255             this.startDate = this.parseDate(this.startDate);
19256         }
19257         this.update();
19258         this.updateNavArrows();
19259     },
19260
19261     setEndDate: function(endDate)
19262     {
19263         this.endDate = endDate || Infinity;
19264         if (this.endDate !== Infinity) {
19265             this.endDate = this.parseDate(this.endDate);
19266         }
19267         this.update();
19268         this.updateNavArrows();
19269     },
19270     
19271     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19272     {
19273         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19274         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19275             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19276         }
19277         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19278             return parseInt(d, 10);
19279         });
19280         this.update();
19281         this.updateNavArrows();
19282     },
19283     
19284     updateNavArrows: function() 
19285     {
19286         if(this.singleMode){
19287             return;
19288         }
19289         
19290         var d = new Date(this.viewDate),
19291         year = d.getUTCFullYear(),
19292         month = d.getUTCMonth();
19293         
19294         Roo.each(this.picker().select('.prev', true).elements, function(v){
19295             v.show();
19296             switch (this.viewMode) {
19297                 case 0:
19298
19299                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19300                         v.hide();
19301                     }
19302                     break;
19303                 case 1:
19304                 case 2:
19305                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19306                         v.hide();
19307                     }
19308                     break;
19309             }
19310         });
19311         
19312         Roo.each(this.picker().select('.next', true).elements, function(v){
19313             v.show();
19314             switch (this.viewMode) {
19315                 case 0:
19316
19317                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19318                         v.hide();
19319                     }
19320                     break;
19321                 case 1:
19322                 case 2:
19323                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19324                         v.hide();
19325                     }
19326                     break;
19327             }
19328         })
19329     },
19330     
19331     moveMonth: function(date, dir)
19332     {
19333         if (!dir) {
19334             return date;
19335         }
19336         var new_date = new Date(date.valueOf()),
19337         day = new_date.getUTCDate(),
19338         month = new_date.getUTCMonth(),
19339         mag = Math.abs(dir),
19340         new_month, test;
19341         dir = dir > 0 ? 1 : -1;
19342         if (mag == 1){
19343             test = dir == -1
19344             // If going back one month, make sure month is not current month
19345             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19346             ? function(){
19347                 return new_date.getUTCMonth() == month;
19348             }
19349             // If going forward one month, make sure month is as expected
19350             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19351             : function(){
19352                 return new_date.getUTCMonth() != new_month;
19353             };
19354             new_month = month + dir;
19355             new_date.setUTCMonth(new_month);
19356             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19357             if (new_month < 0 || new_month > 11) {
19358                 new_month = (new_month + 12) % 12;
19359             }
19360         } else {
19361             // For magnitudes >1, move one month at a time...
19362             for (var i=0; i<mag; i++) {
19363                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19364                 new_date = this.moveMonth(new_date, dir);
19365             }
19366             // ...then reset the day, keeping it in the new month
19367             new_month = new_date.getUTCMonth();
19368             new_date.setUTCDate(day);
19369             test = function(){
19370                 return new_month != new_date.getUTCMonth();
19371             };
19372         }
19373         // Common date-resetting loop -- if date is beyond end of month, make it
19374         // end of month
19375         while (test()){
19376             new_date.setUTCDate(--day);
19377             new_date.setUTCMonth(new_month);
19378         }
19379         return new_date;
19380     },
19381
19382     moveYear: function(date, dir)
19383     {
19384         return this.moveMonth(date, dir*12);
19385     },
19386
19387     dateWithinRange: function(date)
19388     {
19389         return date >= this.startDate && date <= this.endDate;
19390     },
19391
19392     
19393     remove: function() 
19394     {
19395         this.picker().remove();
19396     },
19397     
19398     validateValue : function(value)
19399     {
19400         if(this.getVisibilityEl().hasClass('hidden')){
19401             return true;
19402         }
19403         
19404         if(value.length < 1)  {
19405             if(this.allowBlank){
19406                 return true;
19407             }
19408             return false;
19409         }
19410         
19411         if(value.length < this.minLength){
19412             return false;
19413         }
19414         if(value.length > this.maxLength){
19415             return false;
19416         }
19417         if(this.vtype){
19418             var vt = Roo.form.VTypes;
19419             if(!vt[this.vtype](value, this)){
19420                 return false;
19421             }
19422         }
19423         if(typeof this.validator == "function"){
19424             var msg = this.validator(value);
19425             if(msg !== true){
19426                 return false;
19427             }
19428         }
19429         
19430         if(this.regex && !this.regex.test(value)){
19431             return false;
19432         }
19433         
19434         if(typeof(this.parseDate(value)) == 'undefined'){
19435             return false;
19436         }
19437         
19438         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19439             return false;
19440         }      
19441         
19442         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19443             return false;
19444         } 
19445         
19446         
19447         return true;
19448     },
19449     
19450     reset : function()
19451     {
19452         this.date = this.viewDate = '';
19453         
19454         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19455     }
19456    
19457 });
19458
19459 Roo.apply(Roo.bootstrap.DateField,  {
19460     
19461     head : {
19462         tag: 'thead',
19463         cn: [
19464         {
19465             tag: 'tr',
19466             cn: [
19467             {
19468                 tag: 'th',
19469                 cls: 'prev',
19470                 html: '<i class="fa fa-arrow-left"/>'
19471             },
19472             {
19473                 tag: 'th',
19474                 cls: 'switch',
19475                 colspan: '5'
19476             },
19477             {
19478                 tag: 'th',
19479                 cls: 'next',
19480                 html: '<i class="fa fa-arrow-right"/>'
19481             }
19482
19483             ]
19484         }
19485         ]
19486     },
19487     
19488     content : {
19489         tag: 'tbody',
19490         cn: [
19491         {
19492             tag: 'tr',
19493             cn: [
19494             {
19495                 tag: 'td',
19496                 colspan: '7'
19497             }
19498             ]
19499         }
19500         ]
19501     },
19502     
19503     footer : {
19504         tag: 'tfoot',
19505         cn: [
19506         {
19507             tag: 'tr',
19508             cn: [
19509             {
19510                 tag: 'th',
19511                 colspan: '7',
19512                 cls: 'today'
19513             }
19514                     
19515             ]
19516         }
19517         ]
19518     },
19519     
19520     dates:{
19521         en: {
19522             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19523             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19524             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19525             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19526             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19527             today: "Today"
19528         }
19529     },
19530     
19531     modes: [
19532     {
19533         clsName: 'days',
19534         navFnc: 'Month',
19535         navStep: 1
19536     },
19537     {
19538         clsName: 'months',
19539         navFnc: 'FullYear',
19540         navStep: 1
19541     },
19542     {
19543         clsName: 'years',
19544         navFnc: 'FullYear',
19545         navStep: 10
19546     }]
19547 });
19548
19549 Roo.apply(Roo.bootstrap.DateField,  {
19550   
19551     template : {
19552         tag: 'div',
19553         cls: 'datepicker dropdown-menu roo-dynamic',
19554         cn: [
19555         {
19556             tag: 'div',
19557             cls: 'datepicker-days',
19558             cn: [
19559             {
19560                 tag: 'table',
19561                 cls: 'table-condensed',
19562                 cn:[
19563                 Roo.bootstrap.DateField.head,
19564                 {
19565                     tag: 'tbody'
19566                 },
19567                 Roo.bootstrap.DateField.footer
19568                 ]
19569             }
19570             ]
19571         },
19572         {
19573             tag: 'div',
19574             cls: 'datepicker-months',
19575             cn: [
19576             {
19577                 tag: 'table',
19578                 cls: 'table-condensed',
19579                 cn:[
19580                 Roo.bootstrap.DateField.head,
19581                 Roo.bootstrap.DateField.content,
19582                 Roo.bootstrap.DateField.footer
19583                 ]
19584             }
19585             ]
19586         },
19587         {
19588             tag: 'div',
19589             cls: 'datepicker-years',
19590             cn: [
19591             {
19592                 tag: 'table',
19593                 cls: 'table-condensed',
19594                 cn:[
19595                 Roo.bootstrap.DateField.head,
19596                 Roo.bootstrap.DateField.content,
19597                 Roo.bootstrap.DateField.footer
19598                 ]
19599             }
19600             ]
19601         }
19602         ]
19603     }
19604 });
19605
19606  
19607
19608  /*
19609  * - LGPL
19610  *
19611  * TimeField
19612  * 
19613  */
19614
19615 /**
19616  * @class Roo.bootstrap.TimeField
19617  * @extends Roo.bootstrap.Input
19618  * Bootstrap DateField class
19619  * 
19620  * 
19621  * @constructor
19622  * Create a new TimeField
19623  * @param {Object} config The config object
19624  */
19625
19626 Roo.bootstrap.TimeField = function(config){
19627     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19628     this.addEvents({
19629             /**
19630              * @event show
19631              * Fires when this field show.
19632              * @param {Roo.bootstrap.DateField} thisthis
19633              * @param {Mixed} date The date value
19634              */
19635             show : true,
19636             /**
19637              * @event show
19638              * Fires when this field hide.
19639              * @param {Roo.bootstrap.DateField} this
19640              * @param {Mixed} date The date value
19641              */
19642             hide : true,
19643             /**
19644              * @event select
19645              * Fires when select a date.
19646              * @param {Roo.bootstrap.DateField} this
19647              * @param {Mixed} date The date value
19648              */
19649             select : true
19650         });
19651 };
19652
19653 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19654     
19655     /**
19656      * @cfg {String} format
19657      * The default time format string which can be overriden for localization support.  The format must be
19658      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19659      */
19660     format : "H:i",
19661        
19662     onRender: function(ct, position)
19663     {
19664         
19665         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19666                 
19667         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19668         
19669         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19670         
19671         this.pop = this.picker().select('>.datepicker-time',true).first();
19672         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19673         
19674         this.picker().on('mousedown', this.onMousedown, this);
19675         this.picker().on('click', this.onClick, this);
19676         
19677         this.picker().addClass('datepicker-dropdown');
19678     
19679         this.fillTime();
19680         this.update();
19681             
19682         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19683         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19684         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19685         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19686         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19687         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19688
19689     },
19690     
19691     fireKey: function(e){
19692         if (!this.picker().isVisible()){
19693             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19694                 this.show();
19695             }
19696             return;
19697         }
19698
19699         e.preventDefault();
19700         
19701         switch(e.keyCode){
19702             case 27: // escape
19703                 this.hide();
19704                 break;
19705             case 37: // left
19706             case 39: // right
19707                 this.onTogglePeriod();
19708                 break;
19709             case 38: // up
19710                 this.onIncrementMinutes();
19711                 break;
19712             case 40: // down
19713                 this.onDecrementMinutes();
19714                 break;
19715             case 13: // enter
19716             case 9: // tab
19717                 this.setTime();
19718                 break;
19719         }
19720     },
19721     
19722     onClick: function(e) {
19723         e.stopPropagation();
19724         e.preventDefault();
19725     },
19726     
19727     picker : function()
19728     {
19729         return this.el.select('.datepicker', true).first();
19730     },
19731     
19732     fillTime: function()
19733     {    
19734         var time = this.pop.select('tbody', true).first();
19735         
19736         time.dom.innerHTML = '';
19737         
19738         time.createChild({
19739             tag: 'tr',
19740             cn: [
19741                 {
19742                     tag: 'td',
19743                     cn: [
19744                         {
19745                             tag: 'a',
19746                             href: '#',
19747                             cls: 'btn',
19748                             cn: [
19749                                 {
19750                                     tag: 'span',
19751                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19752                                 }
19753                             ]
19754                         } 
19755                     ]
19756                 },
19757                 {
19758                     tag: 'td',
19759                     cls: 'separator'
19760                 },
19761                 {
19762                     tag: 'td',
19763                     cn: [
19764                         {
19765                             tag: 'a',
19766                             href: '#',
19767                             cls: 'btn',
19768                             cn: [
19769                                 {
19770                                     tag: 'span',
19771                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19772                                 }
19773                             ]
19774                         }
19775                     ]
19776                 },
19777                 {
19778                     tag: 'td',
19779                     cls: 'separator'
19780                 }
19781             ]
19782         });
19783         
19784         time.createChild({
19785             tag: 'tr',
19786             cn: [
19787                 {
19788                     tag: 'td',
19789                     cn: [
19790                         {
19791                             tag: 'span',
19792                             cls: 'timepicker-hour',
19793                             html: '00'
19794                         }  
19795                     ]
19796                 },
19797                 {
19798                     tag: 'td',
19799                     cls: 'separator',
19800                     html: ':'
19801                 },
19802                 {
19803                     tag: 'td',
19804                     cn: [
19805                         {
19806                             tag: 'span',
19807                             cls: 'timepicker-minute',
19808                             html: '00'
19809                         }  
19810                     ]
19811                 },
19812                 {
19813                     tag: 'td',
19814                     cls: 'separator'
19815                 },
19816                 {
19817                     tag: 'td',
19818                     cn: [
19819                         {
19820                             tag: 'button',
19821                             type: 'button',
19822                             cls: 'btn btn-primary period',
19823                             html: 'AM'
19824                             
19825                         }
19826                     ]
19827                 }
19828             ]
19829         });
19830         
19831         time.createChild({
19832             tag: 'tr',
19833             cn: [
19834                 {
19835                     tag: 'td',
19836                     cn: [
19837                         {
19838                             tag: 'a',
19839                             href: '#',
19840                             cls: 'btn',
19841                             cn: [
19842                                 {
19843                                     tag: 'span',
19844                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19845                                 }
19846                             ]
19847                         }
19848                     ]
19849                 },
19850                 {
19851                     tag: 'td',
19852                     cls: 'separator'
19853                 },
19854                 {
19855                     tag: 'td',
19856                     cn: [
19857                         {
19858                             tag: 'a',
19859                             href: '#',
19860                             cls: 'btn',
19861                             cn: [
19862                                 {
19863                                     tag: 'span',
19864                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19865                                 }
19866                             ]
19867                         }
19868                     ]
19869                 },
19870                 {
19871                     tag: 'td',
19872                     cls: 'separator'
19873                 }
19874             ]
19875         });
19876         
19877     },
19878     
19879     update: function()
19880     {
19881         
19882         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19883         
19884         this.fill();
19885     },
19886     
19887     fill: function() 
19888     {
19889         var hours = this.time.getHours();
19890         var minutes = this.time.getMinutes();
19891         var period = 'AM';
19892         
19893         if(hours > 11){
19894             period = 'PM';
19895         }
19896         
19897         if(hours == 0){
19898             hours = 12;
19899         }
19900         
19901         
19902         if(hours > 12){
19903             hours = hours - 12;
19904         }
19905         
19906         if(hours < 10){
19907             hours = '0' + hours;
19908         }
19909         
19910         if(minutes < 10){
19911             minutes = '0' + minutes;
19912         }
19913         
19914         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19915         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19916         this.pop.select('button', true).first().dom.innerHTML = period;
19917         
19918     },
19919     
19920     place: function()
19921     {   
19922         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19923         
19924         var cls = ['bottom'];
19925         
19926         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19927             cls.pop();
19928             cls.push('top');
19929         }
19930         
19931         cls.push('right');
19932         
19933         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19934             cls.pop();
19935             cls.push('left');
19936         }
19937         
19938         this.picker().addClass(cls.join('-'));
19939         
19940         var _this = this;
19941         
19942         Roo.each(cls, function(c){
19943             if(c == 'bottom'){
19944                 _this.picker().setTop(_this.inputEl().getHeight());
19945                 return;
19946             }
19947             if(c == 'top'){
19948                 _this.picker().setTop(0 - _this.picker().getHeight());
19949                 return;
19950             }
19951             
19952             if(c == 'left'){
19953                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19954                 return;
19955             }
19956             if(c == 'right'){
19957                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19958                 return;
19959             }
19960         });
19961         
19962     },
19963   
19964     onFocus : function()
19965     {
19966         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19967         this.show();
19968     },
19969     
19970     onBlur : function()
19971     {
19972         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19973         this.hide();
19974     },
19975     
19976     show : function()
19977     {
19978         this.picker().show();
19979         this.pop.show();
19980         this.update();
19981         this.place();
19982         
19983         this.fireEvent('show', this, this.date);
19984     },
19985     
19986     hide : function()
19987     {
19988         this.picker().hide();
19989         this.pop.hide();
19990         
19991         this.fireEvent('hide', this, this.date);
19992     },
19993     
19994     setTime : function()
19995     {
19996         this.hide();
19997         this.setValue(this.time.format(this.format));
19998         
19999         this.fireEvent('select', this, this.date);
20000         
20001         
20002     },
20003     
20004     onMousedown: function(e){
20005         e.stopPropagation();
20006         e.preventDefault();
20007     },
20008     
20009     onIncrementHours: function()
20010     {
20011         Roo.log('onIncrementHours');
20012         this.time = this.time.add(Date.HOUR, 1);
20013         this.update();
20014         
20015     },
20016     
20017     onDecrementHours: function()
20018     {
20019         Roo.log('onDecrementHours');
20020         this.time = this.time.add(Date.HOUR, -1);
20021         this.update();
20022     },
20023     
20024     onIncrementMinutes: function()
20025     {
20026         Roo.log('onIncrementMinutes');
20027         this.time = this.time.add(Date.MINUTE, 1);
20028         this.update();
20029     },
20030     
20031     onDecrementMinutes: function()
20032     {
20033         Roo.log('onDecrementMinutes');
20034         this.time = this.time.add(Date.MINUTE, -1);
20035         this.update();
20036     },
20037     
20038     onTogglePeriod: function()
20039     {
20040         Roo.log('onTogglePeriod');
20041         this.time = this.time.add(Date.HOUR, 12);
20042         this.update();
20043     }
20044     
20045    
20046 });
20047
20048 Roo.apply(Roo.bootstrap.TimeField,  {
20049     
20050     content : {
20051         tag: 'tbody',
20052         cn: [
20053             {
20054                 tag: 'tr',
20055                 cn: [
20056                 {
20057                     tag: 'td',
20058                     colspan: '7'
20059                 }
20060                 ]
20061             }
20062         ]
20063     },
20064     
20065     footer : {
20066         tag: 'tfoot',
20067         cn: [
20068             {
20069                 tag: 'tr',
20070                 cn: [
20071                 {
20072                     tag: 'th',
20073                     colspan: '7',
20074                     cls: '',
20075                     cn: [
20076                         {
20077                             tag: 'button',
20078                             cls: 'btn btn-info ok',
20079                             html: 'OK'
20080                         }
20081                     ]
20082                 }
20083
20084                 ]
20085             }
20086         ]
20087     }
20088 });
20089
20090 Roo.apply(Roo.bootstrap.TimeField,  {
20091   
20092     template : {
20093         tag: 'div',
20094         cls: 'datepicker dropdown-menu',
20095         cn: [
20096             {
20097                 tag: 'div',
20098                 cls: 'datepicker-time',
20099                 cn: [
20100                 {
20101                     tag: 'table',
20102                     cls: 'table-condensed',
20103                     cn:[
20104                     Roo.bootstrap.TimeField.content,
20105                     Roo.bootstrap.TimeField.footer
20106                     ]
20107                 }
20108                 ]
20109             }
20110         ]
20111     }
20112 });
20113
20114  
20115
20116  /*
20117  * - LGPL
20118  *
20119  * MonthField
20120  * 
20121  */
20122
20123 /**
20124  * @class Roo.bootstrap.MonthField
20125  * @extends Roo.bootstrap.Input
20126  * Bootstrap MonthField class
20127  * 
20128  * @cfg {String} language default en
20129  * 
20130  * @constructor
20131  * Create a new MonthField
20132  * @param {Object} config The config object
20133  */
20134
20135 Roo.bootstrap.MonthField = function(config){
20136     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20137     
20138     this.addEvents({
20139         /**
20140          * @event show
20141          * Fires when this field show.
20142          * @param {Roo.bootstrap.MonthField} this
20143          * @param {Mixed} date The date value
20144          */
20145         show : true,
20146         /**
20147          * @event show
20148          * Fires when this field hide.
20149          * @param {Roo.bootstrap.MonthField} this
20150          * @param {Mixed} date The date value
20151          */
20152         hide : true,
20153         /**
20154          * @event select
20155          * Fires when select a date.
20156          * @param {Roo.bootstrap.MonthField} this
20157          * @param {String} oldvalue The old value
20158          * @param {String} newvalue The new value
20159          */
20160         select : true
20161     });
20162 };
20163
20164 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20165     
20166     onRender: function(ct, position)
20167     {
20168         
20169         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20170         
20171         this.language = this.language || 'en';
20172         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20173         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20174         
20175         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20176         this.isInline = false;
20177         this.isInput = true;
20178         this.component = this.el.select('.add-on', true).first() || false;
20179         this.component = (this.component && this.component.length === 0) ? false : this.component;
20180         this.hasInput = this.component && this.inputEL().length;
20181         
20182         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20183         
20184         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20185         
20186         this.picker().on('mousedown', this.onMousedown, this);
20187         this.picker().on('click', this.onClick, this);
20188         
20189         this.picker().addClass('datepicker-dropdown');
20190         
20191         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20192             v.setStyle('width', '189px');
20193         });
20194         
20195         this.fillMonths();
20196         
20197         this.update();
20198         
20199         if(this.isInline) {
20200             this.show();
20201         }
20202         
20203     },
20204     
20205     setValue: function(v, suppressEvent)
20206     {   
20207         var o = this.getValue();
20208         
20209         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20210         
20211         this.update();
20212
20213         if(suppressEvent !== true){
20214             this.fireEvent('select', this, o, v);
20215         }
20216         
20217     },
20218     
20219     getValue: function()
20220     {
20221         return this.value;
20222     },
20223     
20224     onClick: function(e) 
20225     {
20226         e.stopPropagation();
20227         e.preventDefault();
20228         
20229         var target = e.getTarget();
20230         
20231         if(target.nodeName.toLowerCase() === 'i'){
20232             target = Roo.get(target).dom.parentNode;
20233         }
20234         
20235         var nodeName = target.nodeName;
20236         var className = target.className;
20237         var html = target.innerHTML;
20238         
20239         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20240             return;
20241         }
20242         
20243         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20244         
20245         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20246         
20247         this.hide();
20248                         
20249     },
20250     
20251     picker : function()
20252     {
20253         return this.pickerEl;
20254     },
20255     
20256     fillMonths: function()
20257     {    
20258         var i = 0;
20259         var months = this.picker().select('>.datepicker-months td', true).first();
20260         
20261         months.dom.innerHTML = '';
20262         
20263         while (i < 12) {
20264             var month = {
20265                 tag: 'span',
20266                 cls: 'month',
20267                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20268             };
20269             
20270             months.createChild(month);
20271         }
20272         
20273     },
20274     
20275     update: function()
20276     {
20277         var _this = this;
20278         
20279         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20280             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20281         }
20282         
20283         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20284             e.removeClass('active');
20285             
20286             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20287                 e.addClass('active');
20288             }
20289         })
20290     },
20291     
20292     place: function()
20293     {
20294         if(this.isInline) {
20295             return;
20296         }
20297         
20298         this.picker().removeClass(['bottom', 'top']);
20299         
20300         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20301             /*
20302              * place to the top of element!
20303              *
20304              */
20305             
20306             this.picker().addClass('top');
20307             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20308             
20309             return;
20310         }
20311         
20312         this.picker().addClass('bottom');
20313         
20314         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20315     },
20316     
20317     onFocus : function()
20318     {
20319         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20320         this.show();
20321     },
20322     
20323     onBlur : function()
20324     {
20325         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20326         
20327         var d = this.inputEl().getValue();
20328         
20329         this.setValue(d);
20330                 
20331         this.hide();
20332     },
20333     
20334     show : function()
20335     {
20336         this.picker().show();
20337         this.picker().select('>.datepicker-months', true).first().show();
20338         this.update();
20339         this.place();
20340         
20341         this.fireEvent('show', this, this.date);
20342     },
20343     
20344     hide : function()
20345     {
20346         if(this.isInline) {
20347             return;
20348         }
20349         this.picker().hide();
20350         this.fireEvent('hide', this, this.date);
20351         
20352     },
20353     
20354     onMousedown: function(e)
20355     {
20356         e.stopPropagation();
20357         e.preventDefault();
20358     },
20359     
20360     keyup: function(e)
20361     {
20362         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20363         this.update();
20364     },
20365
20366     fireKey: function(e)
20367     {
20368         if (!this.picker().isVisible()){
20369             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20370                 this.show();
20371             }
20372             return;
20373         }
20374         
20375         var dir;
20376         
20377         switch(e.keyCode){
20378             case 27: // escape
20379                 this.hide();
20380                 e.preventDefault();
20381                 break;
20382             case 37: // left
20383             case 39: // right
20384                 dir = e.keyCode == 37 ? -1 : 1;
20385                 
20386                 this.vIndex = this.vIndex + dir;
20387                 
20388                 if(this.vIndex < 0){
20389                     this.vIndex = 0;
20390                 }
20391                 
20392                 if(this.vIndex > 11){
20393                     this.vIndex = 11;
20394                 }
20395                 
20396                 if(isNaN(this.vIndex)){
20397                     this.vIndex = 0;
20398                 }
20399                 
20400                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20401                 
20402                 break;
20403             case 38: // up
20404             case 40: // down
20405                 
20406                 dir = e.keyCode == 38 ? -1 : 1;
20407                 
20408                 this.vIndex = this.vIndex + dir * 4;
20409                 
20410                 if(this.vIndex < 0){
20411                     this.vIndex = 0;
20412                 }
20413                 
20414                 if(this.vIndex > 11){
20415                     this.vIndex = 11;
20416                 }
20417                 
20418                 if(isNaN(this.vIndex)){
20419                     this.vIndex = 0;
20420                 }
20421                 
20422                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20423                 break;
20424                 
20425             case 13: // enter
20426                 
20427                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20428                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20429                 }
20430                 
20431                 this.hide();
20432                 e.preventDefault();
20433                 break;
20434             case 9: // tab
20435                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20436                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20437                 }
20438                 this.hide();
20439                 break;
20440             case 16: // shift
20441             case 17: // ctrl
20442             case 18: // alt
20443                 break;
20444             default :
20445                 this.hide();
20446                 
20447         }
20448     },
20449     
20450     remove: function() 
20451     {
20452         this.picker().remove();
20453     }
20454    
20455 });
20456
20457 Roo.apply(Roo.bootstrap.MonthField,  {
20458     
20459     content : {
20460         tag: 'tbody',
20461         cn: [
20462         {
20463             tag: 'tr',
20464             cn: [
20465             {
20466                 tag: 'td',
20467                 colspan: '7'
20468             }
20469             ]
20470         }
20471         ]
20472     },
20473     
20474     dates:{
20475         en: {
20476             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20477             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20478         }
20479     }
20480 });
20481
20482 Roo.apply(Roo.bootstrap.MonthField,  {
20483   
20484     template : {
20485         tag: 'div',
20486         cls: 'datepicker dropdown-menu roo-dynamic',
20487         cn: [
20488             {
20489                 tag: 'div',
20490                 cls: 'datepicker-months',
20491                 cn: [
20492                 {
20493                     tag: 'table',
20494                     cls: 'table-condensed',
20495                     cn:[
20496                         Roo.bootstrap.DateField.content
20497                     ]
20498                 }
20499                 ]
20500             }
20501         ]
20502     }
20503 });
20504
20505  
20506
20507  
20508  /*
20509  * - LGPL
20510  *
20511  * CheckBox
20512  * 
20513  */
20514
20515 /**
20516  * @class Roo.bootstrap.CheckBox
20517  * @extends Roo.bootstrap.Input
20518  * Bootstrap CheckBox class
20519  * 
20520  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20521  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20522  * @cfg {String} boxLabel The text that appears beside the checkbox
20523  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20524  * @cfg {Boolean} checked initnal the element
20525  * @cfg {Boolean} inline inline the element (default false)
20526  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20527  * @cfg {String} tooltip label tooltip
20528  * 
20529  * @constructor
20530  * Create a new CheckBox
20531  * @param {Object} config The config object
20532  */
20533
20534 Roo.bootstrap.CheckBox = function(config){
20535     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20536    
20537     this.addEvents({
20538         /**
20539         * @event check
20540         * Fires when the element is checked or unchecked.
20541         * @param {Roo.bootstrap.CheckBox} this This input
20542         * @param {Boolean} checked The new checked value
20543         */
20544        check : true,
20545        /**
20546         * @event click
20547         * Fires when the element is click.
20548         * @param {Roo.bootstrap.CheckBox} this This input
20549         */
20550        click : true
20551     });
20552     
20553 };
20554
20555 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20556   
20557     inputType: 'checkbox',
20558     inputValue: 1,
20559     valueOff: 0,
20560     boxLabel: false,
20561     checked: false,
20562     weight : false,
20563     inline: false,
20564     tooltip : '',
20565     
20566     getAutoCreate : function()
20567     {
20568         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20569         
20570         var id = Roo.id();
20571         
20572         var cfg = {};
20573         
20574         cfg.cls = 'form-group ' + this.inputType; //input-group
20575         
20576         if(this.inline){
20577             cfg.cls += ' ' + this.inputType + '-inline';
20578         }
20579         
20580         var input =  {
20581             tag: 'input',
20582             id : id,
20583             type : this.inputType,
20584             value : this.inputValue,
20585             cls : 'roo-' + this.inputType, //'form-box',
20586             placeholder : this.placeholder || ''
20587             
20588         };
20589         
20590         if(this.inputType != 'radio'){
20591             var hidden =  {
20592                 tag: 'input',
20593                 type : 'hidden',
20594                 cls : 'roo-hidden-value',
20595                 value : this.checked ? this.inputValue : this.valueOff
20596             };
20597         }
20598         
20599             
20600         if (this.weight) { // Validity check?
20601             cfg.cls += " " + this.inputType + "-" + this.weight;
20602         }
20603         
20604         if (this.disabled) {
20605             input.disabled=true;
20606         }
20607         
20608         if(this.checked){
20609             input.checked = this.checked;
20610         }
20611         
20612         if (this.name) {
20613             
20614             input.name = this.name;
20615             
20616             if(this.inputType != 'radio'){
20617                 hidden.name = this.name;
20618                 input.name = '_hidden_' + this.name;
20619             }
20620         }
20621         
20622         if (this.size) {
20623             input.cls += ' input-' + this.size;
20624         }
20625         
20626         var settings=this;
20627         
20628         ['xs','sm','md','lg'].map(function(size){
20629             if (settings[size]) {
20630                 cfg.cls += ' col-' + size + '-' + settings[size];
20631             }
20632         });
20633         
20634         var inputblock = input;
20635          
20636         if (this.before || this.after) {
20637             
20638             inputblock = {
20639                 cls : 'input-group',
20640                 cn :  [] 
20641             };
20642             
20643             if (this.before) {
20644                 inputblock.cn.push({
20645                     tag :'span',
20646                     cls : 'input-group-addon',
20647                     html : this.before
20648                 });
20649             }
20650             
20651             inputblock.cn.push(input);
20652             
20653             if(this.inputType != 'radio'){
20654                 inputblock.cn.push(hidden);
20655             }
20656             
20657             if (this.after) {
20658                 inputblock.cn.push({
20659                     tag :'span',
20660                     cls : 'input-group-addon',
20661                     html : this.after
20662                 });
20663             }
20664             
20665         }
20666         
20667         if (align ==='left' && this.fieldLabel.length) {
20668 //                Roo.log("left and has label");
20669             cfg.cn = [
20670                 {
20671                     tag: 'label',
20672                     'for' :  id,
20673                     cls : 'control-label',
20674                     html : this.fieldLabel
20675                 },
20676                 {
20677                     cls : "", 
20678                     cn: [
20679                         inputblock
20680                     ]
20681                 }
20682             ];
20683             
20684             if(this.labelWidth > 12){
20685                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20686             }
20687             
20688             if(this.labelWidth < 13 && this.labelmd == 0){
20689                 this.labelmd = this.labelWidth;
20690             }
20691             
20692             if(this.labellg > 0){
20693                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20694                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20695             }
20696             
20697             if(this.labelmd > 0){
20698                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20699                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20700             }
20701             
20702             if(this.labelsm > 0){
20703                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20704                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20705             }
20706             
20707             if(this.labelxs > 0){
20708                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20709                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20710             }
20711             
20712         } else if ( this.fieldLabel.length) {
20713 //                Roo.log(" label");
20714                 cfg.cn = [
20715                    
20716                     {
20717                         tag: this.boxLabel ? 'span' : 'label',
20718                         'for': id,
20719                         cls: 'control-label box-input-label',
20720                         //cls : 'input-group-addon',
20721                         html : this.fieldLabel
20722                     },
20723                     
20724                     inputblock
20725                     
20726                 ];
20727
20728         } else {
20729             
20730 //                Roo.log(" no label && no align");
20731                 cfg.cn = [  inputblock ] ;
20732                 
20733                 
20734         }
20735         
20736         if(this.boxLabel){
20737              var boxLabelCfg = {
20738                 tag: 'label',
20739                 //'for': id, // box label is handled by onclick - so no for...
20740                 cls: 'box-label',
20741                 html: this.boxLabel
20742             };
20743             
20744             if(this.tooltip){
20745                 boxLabelCfg.tooltip = this.tooltip;
20746             }
20747              
20748             cfg.cn.push(boxLabelCfg);
20749         }
20750         
20751         if(this.inputType != 'radio'){
20752             cfg.cn.push(hidden);
20753         }
20754         
20755         return cfg;
20756         
20757     },
20758     
20759     /**
20760      * return the real input element.
20761      */
20762     inputEl: function ()
20763     {
20764         return this.el.select('input.roo-' + this.inputType,true).first();
20765     },
20766     hiddenEl: function ()
20767     {
20768         return this.el.select('input.roo-hidden-value',true).first();
20769     },
20770     
20771     labelEl: function()
20772     {
20773         return this.el.select('label.control-label',true).first();
20774     },
20775     /* depricated... */
20776     
20777     label: function()
20778     {
20779         return this.labelEl();
20780     },
20781     
20782     boxLabelEl: function()
20783     {
20784         return this.el.select('label.box-label',true).first();
20785     },
20786     
20787     initEvents : function()
20788     {
20789 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20790         
20791         this.inputEl().on('click', this.onClick,  this);
20792         
20793         if (this.boxLabel) { 
20794             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20795         }
20796         
20797         this.startValue = this.getValue();
20798         
20799         if(this.groupId){
20800             Roo.bootstrap.CheckBox.register(this);
20801         }
20802     },
20803     
20804     onClick : function(e)
20805     {   
20806         if(this.fireEvent('click', this, e) !== false){
20807             this.setChecked(!this.checked);
20808         }
20809         
20810     },
20811     
20812     setChecked : function(state,suppressEvent)
20813     {
20814         this.startValue = this.getValue();
20815
20816         if(this.inputType == 'radio'){
20817             
20818             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20819                 e.dom.checked = false;
20820             });
20821             
20822             this.inputEl().dom.checked = true;
20823             
20824             this.inputEl().dom.value = this.inputValue;
20825             
20826             if(suppressEvent !== true){
20827                 this.fireEvent('check', this, true);
20828             }
20829             
20830             this.validate();
20831             
20832             return;
20833         }
20834         
20835         this.checked = state;
20836         
20837         this.inputEl().dom.checked = state;
20838         
20839         
20840         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20841         
20842         if(suppressEvent !== true){
20843             this.fireEvent('check', this, state);
20844         }
20845         
20846         this.validate();
20847     },
20848     
20849     getValue : function()
20850     {
20851         if(this.inputType == 'radio'){
20852             return this.getGroupValue();
20853         }
20854         
20855         return this.hiddenEl().dom.value;
20856         
20857     },
20858     
20859     getGroupValue : function()
20860     {
20861         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20862             return '';
20863         }
20864         
20865         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20866     },
20867     
20868     setValue : function(v,suppressEvent)
20869     {
20870         if(this.inputType == 'radio'){
20871             this.setGroupValue(v, suppressEvent);
20872             return;
20873         }
20874         
20875         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20876         
20877         this.validate();
20878     },
20879     
20880     setGroupValue : function(v, suppressEvent)
20881     {
20882         this.startValue = this.getValue();
20883         
20884         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20885             e.dom.checked = false;
20886             
20887             if(e.dom.value == v){
20888                 e.dom.checked = true;
20889             }
20890         });
20891         
20892         if(suppressEvent !== true){
20893             this.fireEvent('check', this, true);
20894         }
20895
20896         this.validate();
20897         
20898         return;
20899     },
20900     
20901     validate : function()
20902     {
20903         if(this.getVisibilityEl().hasClass('hidden')){
20904             return true;
20905         }
20906         
20907         if(
20908                 this.disabled || 
20909                 (this.inputType == 'radio' && this.validateRadio()) ||
20910                 (this.inputType == 'checkbox' && this.validateCheckbox())
20911         ){
20912             this.markValid();
20913             return true;
20914         }
20915         
20916         this.markInvalid();
20917         return false;
20918     },
20919     
20920     validateRadio : function()
20921     {
20922         if(this.getVisibilityEl().hasClass('hidden')){
20923             return true;
20924         }
20925         
20926         if(this.allowBlank){
20927             return true;
20928         }
20929         
20930         var valid = false;
20931         
20932         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20933             if(!e.dom.checked){
20934                 return;
20935             }
20936             
20937             valid = true;
20938             
20939             return false;
20940         });
20941         
20942         return valid;
20943     },
20944     
20945     validateCheckbox : function()
20946     {
20947         if(!this.groupId){
20948             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20949             //return (this.getValue() == this.inputValue) ? true : false;
20950         }
20951         
20952         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20953         
20954         if(!group){
20955             return false;
20956         }
20957         
20958         var r = false;
20959         
20960         for(var i in group){
20961             if(group[i].el.isVisible(true)){
20962                 r = false;
20963                 break;
20964             }
20965             
20966             r = true;
20967         }
20968         
20969         for(var i in group){
20970             if(r){
20971                 break;
20972             }
20973             
20974             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20975         }
20976         
20977         return r;
20978     },
20979     
20980     /**
20981      * Mark this field as valid
20982      */
20983     markValid : function()
20984     {
20985         var _this = this;
20986         
20987         this.fireEvent('valid', this);
20988         
20989         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20990         
20991         if(this.groupId){
20992             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20993         }
20994         
20995         if(label){
20996             label.markValid();
20997         }
20998
20999         if(this.inputType == 'radio'){
21000             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21001                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21002                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21003             });
21004             
21005             return;
21006         }
21007
21008         if(!this.groupId){
21009             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21010             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21011             return;
21012         }
21013         
21014         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21015         
21016         if(!group){
21017             return;
21018         }
21019         
21020         for(var i in group){
21021             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21022             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21023         }
21024     },
21025     
21026      /**
21027      * Mark this field as invalid
21028      * @param {String} msg The validation message
21029      */
21030     markInvalid : function(msg)
21031     {
21032         if(this.allowBlank){
21033             return;
21034         }
21035         
21036         var _this = this;
21037         
21038         this.fireEvent('invalid', this, msg);
21039         
21040         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21041         
21042         if(this.groupId){
21043             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21044         }
21045         
21046         if(label){
21047             label.markInvalid();
21048         }
21049             
21050         if(this.inputType == 'radio'){
21051             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21052                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21053                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21054             });
21055             
21056             return;
21057         }
21058         
21059         if(!this.groupId){
21060             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21061             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21062             return;
21063         }
21064         
21065         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21066         
21067         if(!group){
21068             return;
21069         }
21070         
21071         for(var i in group){
21072             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21073             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21074         }
21075         
21076     },
21077     
21078     clearInvalid : function()
21079     {
21080         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21081         
21082         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21083         
21084         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21085         
21086         if (label && label.iconEl) {
21087             label.iconEl.removeClass(label.validClass);
21088             label.iconEl.removeClass(label.invalidClass);
21089         }
21090     },
21091     
21092     disable : function()
21093     {
21094         if(this.inputType != 'radio'){
21095             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21096             return;
21097         }
21098         
21099         var _this = this;
21100         
21101         if(this.rendered){
21102             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21103                 _this.getActionEl().addClass(this.disabledClass);
21104                 e.dom.disabled = true;
21105             });
21106         }
21107         
21108         this.disabled = true;
21109         this.fireEvent("disable", this);
21110         return this;
21111     },
21112
21113     enable : function()
21114     {
21115         if(this.inputType != 'radio'){
21116             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21117             return;
21118         }
21119         
21120         var _this = this;
21121         
21122         if(this.rendered){
21123             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21124                 _this.getActionEl().removeClass(this.disabledClass);
21125                 e.dom.disabled = false;
21126             });
21127         }
21128         
21129         this.disabled = false;
21130         this.fireEvent("enable", this);
21131         return this;
21132     },
21133     
21134     setBoxLabel : function(v)
21135     {
21136         this.boxLabel = v;
21137         
21138         if(this.rendered){
21139             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21140         }
21141     }
21142
21143 });
21144
21145 Roo.apply(Roo.bootstrap.CheckBox, {
21146     
21147     groups: {},
21148     
21149      /**
21150     * register a CheckBox Group
21151     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21152     */
21153     register : function(checkbox)
21154     {
21155         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21156             this.groups[checkbox.groupId] = {};
21157         }
21158         
21159         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21160             return;
21161         }
21162         
21163         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21164         
21165     },
21166     /**
21167     * fetch a CheckBox Group based on the group ID
21168     * @param {string} the group ID
21169     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21170     */
21171     get: function(groupId) {
21172         if (typeof(this.groups[groupId]) == 'undefined') {
21173             return false;
21174         }
21175         
21176         return this.groups[groupId] ;
21177     }
21178     
21179     
21180 });
21181 /*
21182  * - LGPL
21183  *
21184  * RadioItem
21185  * 
21186  */
21187
21188 /**
21189  * @class Roo.bootstrap.Radio
21190  * @extends Roo.bootstrap.Component
21191  * Bootstrap Radio class
21192  * @cfg {String} boxLabel - the label associated
21193  * @cfg {String} value - the value of radio
21194  * 
21195  * @constructor
21196  * Create a new Radio
21197  * @param {Object} config The config object
21198  */
21199 Roo.bootstrap.Radio = function(config){
21200     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21201     
21202 };
21203
21204 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21205     
21206     boxLabel : '',
21207     
21208     value : '',
21209     
21210     getAutoCreate : function()
21211     {
21212         var cfg = {
21213             tag : 'div',
21214             cls : 'form-group radio',
21215             cn : [
21216                 {
21217                     tag : 'label',
21218                     cls : 'box-label',
21219                     html : this.boxLabel
21220                 }
21221             ]
21222         };
21223         
21224         return cfg;
21225     },
21226     
21227     initEvents : function() 
21228     {
21229         this.parent().register(this);
21230         
21231         this.el.on('click', this.onClick, this);
21232         
21233     },
21234     
21235     onClick : function(e)
21236     {
21237         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21238             this.setChecked(true);
21239         }
21240     },
21241     
21242     setChecked : function(state, suppressEvent)
21243     {
21244         this.parent().setValue(this.value, suppressEvent);
21245         
21246     },
21247     
21248     setBoxLabel : function(v)
21249     {
21250         this.boxLabel = v;
21251         
21252         if(this.rendered){
21253             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21254         }
21255     }
21256     
21257 });
21258  
21259
21260  /*
21261  * - LGPL
21262  *
21263  * Input
21264  * 
21265  */
21266
21267 /**
21268  * @class Roo.bootstrap.SecurePass
21269  * @extends Roo.bootstrap.Input
21270  * Bootstrap SecurePass class
21271  *
21272  * 
21273  * @constructor
21274  * Create a new SecurePass
21275  * @param {Object} config The config object
21276  */
21277  
21278 Roo.bootstrap.SecurePass = function (config) {
21279     // these go here, so the translation tool can replace them..
21280     this.errors = {
21281         PwdEmpty: "Please type a password, and then retype it to confirm.",
21282         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21283         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21284         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21285         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21286         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21287         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21288         TooWeak: "Your password is Too Weak."
21289     },
21290     this.meterLabel = "Password strength:";
21291     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21292     this.meterClass = [
21293         "roo-password-meter-tooweak", 
21294         "roo-password-meter-weak", 
21295         "roo-password-meter-medium", 
21296         "roo-password-meter-strong", 
21297         "roo-password-meter-grey"
21298     ];
21299     
21300     this.errors = {};
21301     
21302     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21303 }
21304
21305 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21306     /**
21307      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21308      * {
21309      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21310      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21311      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21312      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21313      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21314      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21315      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21316      * })
21317      */
21318     // private
21319     
21320     meterWidth: 300,
21321     errorMsg :'',    
21322     errors: false,
21323     imageRoot: '/',
21324     /**
21325      * @cfg {String/Object} Label for the strength meter (defaults to
21326      * 'Password strength:')
21327      */
21328     // private
21329     meterLabel: '',
21330     /**
21331      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21332      * ['Weak', 'Medium', 'Strong'])
21333      */
21334     // private    
21335     pwdStrengths: false,    
21336     // private
21337     strength: 0,
21338     // private
21339     _lastPwd: null,
21340     // private
21341     kCapitalLetter: 0,
21342     kSmallLetter: 1,
21343     kDigit: 2,
21344     kPunctuation: 3,
21345     
21346     insecure: false,
21347     // private
21348     initEvents: function ()
21349     {
21350         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21351
21352         if (this.el.is('input[type=password]') && Roo.isSafari) {
21353             this.el.on('keydown', this.SafariOnKeyDown, this);
21354         }
21355
21356         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21357     },
21358     // private
21359     onRender: function (ct, position)
21360     {
21361         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21362         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21363         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21364
21365         this.trigger.createChild({
21366                    cn: [
21367                     {
21368                     //id: 'PwdMeter',
21369                     tag: 'div',
21370                     cls: 'roo-password-meter-grey col-xs-12',
21371                     style: {
21372                         //width: 0,
21373                         //width: this.meterWidth + 'px'                                                
21374                         }
21375                     },
21376                     {                            
21377                          cls: 'roo-password-meter-text'                          
21378                     }
21379                 ]            
21380         });
21381
21382          
21383         if (this.hideTrigger) {
21384             this.trigger.setDisplayed(false);
21385         }
21386         this.setSize(this.width || '', this.height || '');
21387     },
21388     // private
21389     onDestroy: function ()
21390     {
21391         if (this.trigger) {
21392             this.trigger.removeAllListeners();
21393             this.trigger.remove();
21394         }
21395         if (this.wrap) {
21396             this.wrap.remove();
21397         }
21398         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21399     },
21400     // private
21401     checkStrength: function ()
21402     {
21403         var pwd = this.inputEl().getValue();
21404         if (pwd == this._lastPwd) {
21405             return;
21406         }
21407
21408         var strength;
21409         if (this.ClientSideStrongPassword(pwd)) {
21410             strength = 3;
21411         } else if (this.ClientSideMediumPassword(pwd)) {
21412             strength = 2;
21413         } else if (this.ClientSideWeakPassword(pwd)) {
21414             strength = 1;
21415         } else {
21416             strength = 0;
21417         }
21418         
21419         Roo.log('strength1: ' + strength);
21420         
21421         //var pm = this.trigger.child('div/div/div').dom;
21422         var pm = this.trigger.child('div/div');
21423         pm.removeClass(this.meterClass);
21424         pm.addClass(this.meterClass[strength]);
21425                 
21426         
21427         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21428                 
21429         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21430         
21431         this._lastPwd = pwd;
21432     },
21433     reset: function ()
21434     {
21435         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21436         
21437         this._lastPwd = '';
21438         
21439         var pm = this.trigger.child('div/div');
21440         pm.removeClass(this.meterClass);
21441         pm.addClass('roo-password-meter-grey');        
21442         
21443         
21444         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21445         
21446         pt.innerHTML = '';
21447         this.inputEl().dom.type='password';
21448     },
21449     // private
21450     validateValue: function (value)
21451     {
21452         
21453         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21454             return false;
21455         }
21456         if (value.length == 0) {
21457             if (this.allowBlank) {
21458                 this.clearInvalid();
21459                 return true;
21460             }
21461
21462             this.markInvalid(this.errors.PwdEmpty);
21463             this.errorMsg = this.errors.PwdEmpty;
21464             return false;
21465         }
21466         
21467         if(this.insecure){
21468             return true;
21469         }
21470         
21471         if ('[\x21-\x7e]*'.match(value)) {
21472             this.markInvalid(this.errors.PwdBadChar);
21473             this.errorMsg = this.errors.PwdBadChar;
21474             return false;
21475         }
21476         if (value.length < 6) {
21477             this.markInvalid(this.errors.PwdShort);
21478             this.errorMsg = this.errors.PwdShort;
21479             return false;
21480         }
21481         if (value.length > 16) {
21482             this.markInvalid(this.errors.PwdLong);
21483             this.errorMsg = this.errors.PwdLong;
21484             return false;
21485         }
21486         var strength;
21487         if (this.ClientSideStrongPassword(value)) {
21488             strength = 3;
21489         } else if (this.ClientSideMediumPassword(value)) {
21490             strength = 2;
21491         } else if (this.ClientSideWeakPassword(value)) {
21492             strength = 1;
21493         } else {
21494             strength = 0;
21495         }
21496
21497         
21498         if (strength < 2) {
21499             //this.markInvalid(this.errors.TooWeak);
21500             this.errorMsg = this.errors.TooWeak;
21501             //return false;
21502         }
21503         
21504         
21505         console.log('strength2: ' + strength);
21506         
21507         //var pm = this.trigger.child('div/div/div').dom;
21508         
21509         var pm = this.trigger.child('div/div');
21510         pm.removeClass(this.meterClass);
21511         pm.addClass(this.meterClass[strength]);
21512                 
21513         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21514                 
21515         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21516         
21517         this.errorMsg = ''; 
21518         return true;
21519     },
21520     // private
21521     CharacterSetChecks: function (type)
21522     {
21523         this.type = type;
21524         this.fResult = false;
21525     },
21526     // private
21527     isctype: function (character, type)
21528     {
21529         switch (type) {  
21530             case this.kCapitalLetter:
21531                 if (character >= 'A' && character <= 'Z') {
21532                     return true;
21533                 }
21534                 break;
21535             
21536             case this.kSmallLetter:
21537                 if (character >= 'a' && character <= 'z') {
21538                     return true;
21539                 }
21540                 break;
21541             
21542             case this.kDigit:
21543                 if (character >= '0' && character <= '9') {
21544                     return true;
21545                 }
21546                 break;
21547             
21548             case this.kPunctuation:
21549                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21550                     return true;
21551                 }
21552                 break;
21553             
21554             default:
21555                 return false;
21556         }
21557
21558     },
21559     // private
21560     IsLongEnough: function (pwd, size)
21561     {
21562         return !(pwd == null || isNaN(size) || pwd.length < size);
21563     },
21564     // private
21565     SpansEnoughCharacterSets: function (word, nb)
21566     {
21567         if (!this.IsLongEnough(word, nb))
21568         {
21569             return false;
21570         }
21571
21572         var characterSetChecks = new Array(
21573             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21574             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21575         );
21576         
21577         for (var index = 0; index < word.length; ++index) {
21578             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21579                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21580                     characterSetChecks[nCharSet].fResult = true;
21581                     break;
21582                 }
21583             }
21584         }
21585
21586         var nCharSets = 0;
21587         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21588             if (characterSetChecks[nCharSet].fResult) {
21589                 ++nCharSets;
21590             }
21591         }
21592
21593         if (nCharSets < nb) {
21594             return false;
21595         }
21596         return true;
21597     },
21598     // private
21599     ClientSideStrongPassword: function (pwd)
21600     {
21601         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21602     },
21603     // private
21604     ClientSideMediumPassword: function (pwd)
21605     {
21606         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21607     },
21608     // private
21609     ClientSideWeakPassword: function (pwd)
21610     {
21611         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21612     }
21613           
21614 })//<script type="text/javascript">
21615
21616 /*
21617  * Based  Ext JS Library 1.1.1
21618  * Copyright(c) 2006-2007, Ext JS, LLC.
21619  * LGPL
21620  *
21621  */
21622  
21623 /**
21624  * @class Roo.HtmlEditorCore
21625  * @extends Roo.Component
21626  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21627  *
21628  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21629  */
21630
21631 Roo.HtmlEditorCore = function(config){
21632     
21633     
21634     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21635     
21636     
21637     this.addEvents({
21638         /**
21639          * @event initialize
21640          * Fires when the editor is fully initialized (including the iframe)
21641          * @param {Roo.HtmlEditorCore} this
21642          */
21643         initialize: true,
21644         /**
21645          * @event activate
21646          * Fires when the editor is first receives the focus. Any insertion must wait
21647          * until after this event.
21648          * @param {Roo.HtmlEditorCore} this
21649          */
21650         activate: true,
21651          /**
21652          * @event beforesync
21653          * Fires before the textarea is updated with content from the editor iframe. Return false
21654          * to cancel the sync.
21655          * @param {Roo.HtmlEditorCore} this
21656          * @param {String} html
21657          */
21658         beforesync: true,
21659          /**
21660          * @event beforepush
21661          * Fires before the iframe editor is updated with content from the textarea. Return false
21662          * to cancel the push.
21663          * @param {Roo.HtmlEditorCore} this
21664          * @param {String} html
21665          */
21666         beforepush: true,
21667          /**
21668          * @event sync
21669          * Fires when the textarea is updated with content from the editor iframe.
21670          * @param {Roo.HtmlEditorCore} this
21671          * @param {String} html
21672          */
21673         sync: true,
21674          /**
21675          * @event push
21676          * Fires when the iframe editor is updated with content from the textarea.
21677          * @param {Roo.HtmlEditorCore} this
21678          * @param {String} html
21679          */
21680         push: true,
21681         
21682         /**
21683          * @event editorevent
21684          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21685          * @param {Roo.HtmlEditorCore} this
21686          */
21687         editorevent: true
21688         
21689     });
21690     
21691     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21692     
21693     // defaults : white / black...
21694     this.applyBlacklists();
21695     
21696     
21697     
21698 };
21699
21700
21701 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21702
21703
21704      /**
21705      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21706      */
21707     
21708     owner : false,
21709     
21710      /**
21711      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21712      *                        Roo.resizable.
21713      */
21714     resizable : false,
21715      /**
21716      * @cfg {Number} height (in pixels)
21717      */   
21718     height: 300,
21719    /**
21720      * @cfg {Number} width (in pixels)
21721      */   
21722     width: 500,
21723     
21724     /**
21725      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21726      * 
21727      */
21728     stylesheets: false,
21729     
21730     // id of frame..
21731     frameId: false,
21732     
21733     // private properties
21734     validationEvent : false,
21735     deferHeight: true,
21736     initialized : false,
21737     activated : false,
21738     sourceEditMode : false,
21739     onFocus : Roo.emptyFn,
21740     iframePad:3,
21741     hideMode:'offsets',
21742     
21743     clearUp: true,
21744     
21745     // blacklist + whitelisted elements..
21746     black: false,
21747     white: false,
21748      
21749     bodyCls : '',
21750
21751     /**
21752      * Protected method that will not generally be called directly. It
21753      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21754      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21755      */
21756     getDocMarkup : function(){
21757         // body styles..
21758         var st = '';
21759         
21760         // inherit styels from page...?? 
21761         if (this.stylesheets === false) {
21762             
21763             Roo.get(document.head).select('style').each(function(node) {
21764                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21765             });
21766             
21767             Roo.get(document.head).select('link').each(function(node) { 
21768                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21769             });
21770             
21771         } else if (!this.stylesheets.length) {
21772                 // simple..
21773                 st = '<style type="text/css">' +
21774                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21775                    '</style>';
21776         } else { 
21777             st = '<style type="text/css">' +
21778                     this.stylesheets +
21779                 '</style>';
21780         }
21781         
21782         st +=  '<style type="text/css">' +
21783             'IMG { cursor: pointer } ' +
21784         '</style>';
21785
21786         var cls = 'roo-htmleditor-body';
21787         
21788         if(this.bodyCls.length){
21789             cls += ' ' + this.bodyCls;
21790         }
21791         
21792         return '<html><head>' + st  +
21793             //<style type="text/css">' +
21794             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21795             //'</style>' +
21796             ' </head><body class="' +  cls + '"></body></html>';
21797     },
21798
21799     // private
21800     onRender : function(ct, position)
21801     {
21802         var _t = this;
21803         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21804         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21805         
21806         
21807         this.el.dom.style.border = '0 none';
21808         this.el.dom.setAttribute('tabIndex', -1);
21809         this.el.addClass('x-hidden hide');
21810         
21811         
21812         
21813         if(Roo.isIE){ // fix IE 1px bogus margin
21814             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21815         }
21816        
21817         
21818         this.frameId = Roo.id();
21819         
21820          
21821         
21822         var iframe = this.owner.wrap.createChild({
21823             tag: 'iframe',
21824             cls: 'form-control', // bootstrap..
21825             id: this.frameId,
21826             name: this.frameId,
21827             frameBorder : 'no',
21828             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21829         }, this.el
21830         );
21831         
21832         
21833         this.iframe = iframe.dom;
21834
21835          this.assignDocWin();
21836         
21837         this.doc.designMode = 'on';
21838        
21839         this.doc.open();
21840         this.doc.write(this.getDocMarkup());
21841         this.doc.close();
21842
21843         
21844         var task = { // must defer to wait for browser to be ready
21845             run : function(){
21846                 //console.log("run task?" + this.doc.readyState);
21847                 this.assignDocWin();
21848                 if(this.doc.body || this.doc.readyState == 'complete'){
21849                     try {
21850                         this.doc.designMode="on";
21851                     } catch (e) {
21852                         return;
21853                     }
21854                     Roo.TaskMgr.stop(task);
21855                     this.initEditor.defer(10, this);
21856                 }
21857             },
21858             interval : 10,
21859             duration: 10000,
21860             scope: this
21861         };
21862         Roo.TaskMgr.start(task);
21863
21864     },
21865
21866     // private
21867     onResize : function(w, h)
21868     {
21869          Roo.log('resize: ' +w + ',' + h );
21870         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21871         if(!this.iframe){
21872             return;
21873         }
21874         if(typeof w == 'number'){
21875             
21876             this.iframe.style.width = w + 'px';
21877         }
21878         if(typeof h == 'number'){
21879             
21880             this.iframe.style.height = h + 'px';
21881             if(this.doc){
21882                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21883             }
21884         }
21885         
21886     },
21887
21888     /**
21889      * Toggles the editor between standard and source edit mode.
21890      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21891      */
21892     toggleSourceEdit : function(sourceEditMode){
21893         
21894         this.sourceEditMode = sourceEditMode === true;
21895         
21896         if(this.sourceEditMode){
21897  
21898             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21899             
21900         }else{
21901             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21902             //this.iframe.className = '';
21903             this.deferFocus();
21904         }
21905         //this.setSize(this.owner.wrap.getSize());
21906         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21907     },
21908
21909     
21910   
21911
21912     /**
21913      * Protected method that will not generally be called directly. If you need/want
21914      * custom HTML cleanup, this is the method you should override.
21915      * @param {String} html The HTML to be cleaned
21916      * return {String} The cleaned HTML
21917      */
21918     cleanHtml : function(html){
21919         html = String(html);
21920         if(html.length > 5){
21921             if(Roo.isSafari){ // strip safari nonsense
21922                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21923             }
21924         }
21925         if(html == '&nbsp;'){
21926             html = '';
21927         }
21928         return html;
21929     },
21930
21931     /**
21932      * HTML Editor -> Textarea
21933      * Protected method that will not generally be called directly. Syncs the contents
21934      * of the editor iframe with the textarea.
21935      */
21936     syncValue : function(){
21937         if(this.initialized){
21938             var bd = (this.doc.body || this.doc.documentElement);
21939             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21940             var html = bd.innerHTML;
21941             if(Roo.isSafari){
21942                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21943                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21944                 if(m && m[1]){
21945                     html = '<div style="'+m[0]+'">' + html + '</div>';
21946                 }
21947             }
21948             html = this.cleanHtml(html);
21949             // fix up the special chars.. normaly like back quotes in word...
21950             // however we do not want to do this with chinese..
21951             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21952                 var cc = b.charCodeAt();
21953                 if (
21954                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21955                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21956                     (cc >= 0xf900 && cc < 0xfb00 )
21957                 ) {
21958                         return b;
21959                 }
21960                 return "&#"+cc+";" 
21961             });
21962             if(this.owner.fireEvent('beforesync', this, html) !== false){
21963                 this.el.dom.value = html;
21964                 this.owner.fireEvent('sync', this, html);
21965             }
21966         }
21967     },
21968
21969     /**
21970      * Protected method that will not generally be called directly. Pushes the value of the textarea
21971      * into the iframe editor.
21972      */
21973     pushValue : function(){
21974         if(this.initialized){
21975             var v = this.el.dom.value.trim();
21976             
21977 //            if(v.length < 1){
21978 //                v = '&#160;';
21979 //            }
21980             
21981             if(this.owner.fireEvent('beforepush', this, v) !== false){
21982                 var d = (this.doc.body || this.doc.documentElement);
21983                 d.innerHTML = v;
21984                 this.cleanUpPaste();
21985                 this.el.dom.value = d.innerHTML;
21986                 this.owner.fireEvent('push', this, v);
21987             }
21988         }
21989     },
21990
21991     // private
21992     deferFocus : function(){
21993         this.focus.defer(10, this);
21994     },
21995
21996     // doc'ed in Field
21997     focus : function(){
21998         if(this.win && !this.sourceEditMode){
21999             this.win.focus();
22000         }else{
22001             this.el.focus();
22002         }
22003     },
22004     
22005     assignDocWin: function()
22006     {
22007         var iframe = this.iframe;
22008         
22009          if(Roo.isIE){
22010             this.doc = iframe.contentWindow.document;
22011             this.win = iframe.contentWindow;
22012         } else {
22013 //            if (!Roo.get(this.frameId)) {
22014 //                return;
22015 //            }
22016 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22017 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22018             
22019             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22020                 return;
22021             }
22022             
22023             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22024             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22025         }
22026     },
22027     
22028     // private
22029     initEditor : function(){
22030         //console.log("INIT EDITOR");
22031         this.assignDocWin();
22032         
22033         
22034         
22035         this.doc.designMode="on";
22036         this.doc.open();
22037         this.doc.write(this.getDocMarkup());
22038         this.doc.close();
22039         
22040         var dbody = (this.doc.body || this.doc.documentElement);
22041         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22042         // this copies styles from the containing element into thsi one..
22043         // not sure why we need all of this..
22044         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22045         
22046         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22047         //ss['background-attachment'] = 'fixed'; // w3c
22048         dbody.bgProperties = 'fixed'; // ie
22049         //Roo.DomHelper.applyStyles(dbody, ss);
22050         Roo.EventManager.on(this.doc, {
22051             //'mousedown': this.onEditorEvent,
22052             'mouseup': this.onEditorEvent,
22053             'dblclick': this.onEditorEvent,
22054             'click': this.onEditorEvent,
22055             'keyup': this.onEditorEvent,
22056             buffer:100,
22057             scope: this
22058         });
22059         if(Roo.isGecko){
22060             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22061         }
22062         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22063             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22064         }
22065         this.initialized = true;
22066
22067         this.owner.fireEvent('initialize', this);
22068         this.pushValue();
22069     },
22070
22071     // private
22072     onDestroy : function(){
22073         
22074         
22075         
22076         if(this.rendered){
22077             
22078             //for (var i =0; i < this.toolbars.length;i++) {
22079             //    // fixme - ask toolbars for heights?
22080             //    this.toolbars[i].onDestroy();
22081            // }
22082             
22083             //this.wrap.dom.innerHTML = '';
22084             //this.wrap.remove();
22085         }
22086     },
22087
22088     // private
22089     onFirstFocus : function(){
22090         
22091         this.assignDocWin();
22092         
22093         
22094         this.activated = true;
22095          
22096     
22097         if(Roo.isGecko){ // prevent silly gecko errors
22098             this.win.focus();
22099             var s = this.win.getSelection();
22100             if(!s.focusNode || s.focusNode.nodeType != 3){
22101                 var r = s.getRangeAt(0);
22102                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22103                 r.collapse(true);
22104                 this.deferFocus();
22105             }
22106             try{
22107                 this.execCmd('useCSS', true);
22108                 this.execCmd('styleWithCSS', false);
22109             }catch(e){}
22110         }
22111         this.owner.fireEvent('activate', this);
22112     },
22113
22114     // private
22115     adjustFont: function(btn){
22116         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22117         //if(Roo.isSafari){ // safari
22118         //    adjust *= 2;
22119        // }
22120         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22121         if(Roo.isSafari){ // safari
22122             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22123             v =  (v < 10) ? 10 : v;
22124             v =  (v > 48) ? 48 : v;
22125             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22126             
22127         }
22128         
22129         
22130         v = Math.max(1, v+adjust);
22131         
22132         this.execCmd('FontSize', v  );
22133     },
22134
22135     onEditorEvent : function(e)
22136     {
22137         this.owner.fireEvent('editorevent', this, e);
22138       //  this.updateToolbar();
22139         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22140     },
22141
22142     insertTag : function(tg)
22143     {
22144         // could be a bit smarter... -> wrap the current selected tRoo..
22145         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22146             
22147             range = this.createRange(this.getSelection());
22148             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22149             wrappingNode.appendChild(range.extractContents());
22150             range.insertNode(wrappingNode);
22151
22152             return;
22153             
22154             
22155             
22156         }
22157         this.execCmd("formatblock",   tg);
22158         
22159     },
22160     
22161     insertText : function(txt)
22162     {
22163         
22164         
22165         var range = this.createRange();
22166         range.deleteContents();
22167                //alert(Sender.getAttribute('label'));
22168                
22169         range.insertNode(this.doc.createTextNode(txt));
22170     } ,
22171     
22172      
22173
22174     /**
22175      * Executes a Midas editor command on the editor document and performs necessary focus and
22176      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22177      * @param {String} cmd The Midas command
22178      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22179      */
22180     relayCmd : function(cmd, value){
22181         this.win.focus();
22182         this.execCmd(cmd, value);
22183         this.owner.fireEvent('editorevent', this);
22184         //this.updateToolbar();
22185         this.owner.deferFocus();
22186     },
22187
22188     /**
22189      * Executes a Midas editor command directly on the editor document.
22190      * For visual commands, you should use {@link #relayCmd} instead.
22191      * <b>This should only be called after the editor is initialized.</b>
22192      * @param {String} cmd The Midas command
22193      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22194      */
22195     execCmd : function(cmd, value){
22196         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22197         this.syncValue();
22198     },
22199  
22200  
22201    
22202     /**
22203      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22204      * to insert tRoo.
22205      * @param {String} text | dom node.. 
22206      */
22207     insertAtCursor : function(text)
22208     {
22209         
22210         if(!this.activated){
22211             return;
22212         }
22213         /*
22214         if(Roo.isIE){
22215             this.win.focus();
22216             var r = this.doc.selection.createRange();
22217             if(r){
22218                 r.collapse(true);
22219                 r.pasteHTML(text);
22220                 this.syncValue();
22221                 this.deferFocus();
22222             
22223             }
22224             return;
22225         }
22226         */
22227         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22228             this.win.focus();
22229             
22230             
22231             // from jquery ui (MIT licenced)
22232             var range, node;
22233             var win = this.win;
22234             
22235             if (win.getSelection && win.getSelection().getRangeAt) {
22236                 range = win.getSelection().getRangeAt(0);
22237                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22238                 range.insertNode(node);
22239             } else if (win.document.selection && win.document.selection.createRange) {
22240                 // no firefox support
22241                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22242                 win.document.selection.createRange().pasteHTML(txt);
22243             } else {
22244                 // no firefox support
22245                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22246                 this.execCmd('InsertHTML', txt);
22247             } 
22248             
22249             this.syncValue();
22250             
22251             this.deferFocus();
22252         }
22253     },
22254  // private
22255     mozKeyPress : function(e){
22256         if(e.ctrlKey){
22257             var c = e.getCharCode(), cmd;
22258           
22259             if(c > 0){
22260                 c = String.fromCharCode(c).toLowerCase();
22261                 switch(c){
22262                     case 'b':
22263                         cmd = 'bold';
22264                         break;
22265                     case 'i':
22266                         cmd = 'italic';
22267                         break;
22268                     
22269                     case 'u':
22270                         cmd = 'underline';
22271                         break;
22272                     
22273                     case 'v':
22274                         this.cleanUpPaste.defer(100, this);
22275                         return;
22276                         
22277                 }
22278                 if(cmd){
22279                     this.win.focus();
22280                     this.execCmd(cmd);
22281                     this.deferFocus();
22282                     e.preventDefault();
22283                 }
22284                 
22285             }
22286         }
22287     },
22288
22289     // private
22290     fixKeys : function(){ // load time branching for fastest keydown performance
22291         if(Roo.isIE){
22292             return function(e){
22293                 var k = e.getKey(), r;
22294                 if(k == e.TAB){
22295                     e.stopEvent();
22296                     r = this.doc.selection.createRange();
22297                     if(r){
22298                         r.collapse(true);
22299                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22300                         this.deferFocus();
22301                     }
22302                     return;
22303                 }
22304                 
22305                 if(k == e.ENTER){
22306                     r = this.doc.selection.createRange();
22307                     if(r){
22308                         var target = r.parentElement();
22309                         if(!target || target.tagName.toLowerCase() != 'li'){
22310                             e.stopEvent();
22311                             r.pasteHTML('<br />');
22312                             r.collapse(false);
22313                             r.select();
22314                         }
22315                     }
22316                 }
22317                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22318                     this.cleanUpPaste.defer(100, this);
22319                     return;
22320                 }
22321                 
22322                 
22323             };
22324         }else if(Roo.isOpera){
22325             return function(e){
22326                 var k = e.getKey();
22327                 if(k == e.TAB){
22328                     e.stopEvent();
22329                     this.win.focus();
22330                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22331                     this.deferFocus();
22332                 }
22333                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22334                     this.cleanUpPaste.defer(100, this);
22335                     return;
22336                 }
22337                 
22338             };
22339         }else if(Roo.isSafari){
22340             return function(e){
22341                 var k = e.getKey();
22342                 
22343                 if(k == e.TAB){
22344                     e.stopEvent();
22345                     this.execCmd('InsertText','\t');
22346                     this.deferFocus();
22347                     return;
22348                 }
22349                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22350                     this.cleanUpPaste.defer(100, this);
22351                     return;
22352                 }
22353                 
22354              };
22355         }
22356     }(),
22357     
22358     getAllAncestors: function()
22359     {
22360         var p = this.getSelectedNode();
22361         var a = [];
22362         if (!p) {
22363             a.push(p); // push blank onto stack..
22364             p = this.getParentElement();
22365         }
22366         
22367         
22368         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22369             a.push(p);
22370             p = p.parentNode;
22371         }
22372         a.push(this.doc.body);
22373         return a;
22374     },
22375     lastSel : false,
22376     lastSelNode : false,
22377     
22378     
22379     getSelection : function() 
22380     {
22381         this.assignDocWin();
22382         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22383     },
22384     
22385     getSelectedNode: function() 
22386     {
22387         // this may only work on Gecko!!!
22388         
22389         // should we cache this!!!!
22390         
22391         
22392         
22393          
22394         var range = this.createRange(this.getSelection()).cloneRange();
22395         
22396         if (Roo.isIE) {
22397             var parent = range.parentElement();
22398             while (true) {
22399                 var testRange = range.duplicate();
22400                 testRange.moveToElementText(parent);
22401                 if (testRange.inRange(range)) {
22402                     break;
22403                 }
22404                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22405                     break;
22406                 }
22407                 parent = parent.parentElement;
22408             }
22409             return parent;
22410         }
22411         
22412         // is ancestor a text element.
22413         var ac =  range.commonAncestorContainer;
22414         if (ac.nodeType == 3) {
22415             ac = ac.parentNode;
22416         }
22417         
22418         var ar = ac.childNodes;
22419          
22420         var nodes = [];
22421         var other_nodes = [];
22422         var has_other_nodes = false;
22423         for (var i=0;i<ar.length;i++) {
22424             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22425                 continue;
22426             }
22427             // fullly contained node.
22428             
22429             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22430                 nodes.push(ar[i]);
22431                 continue;
22432             }
22433             
22434             // probably selected..
22435             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22436                 other_nodes.push(ar[i]);
22437                 continue;
22438             }
22439             // outer..
22440             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22441                 continue;
22442             }
22443             
22444             
22445             has_other_nodes = true;
22446         }
22447         if (!nodes.length && other_nodes.length) {
22448             nodes= other_nodes;
22449         }
22450         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22451             return false;
22452         }
22453         
22454         return nodes[0];
22455     },
22456     createRange: function(sel)
22457     {
22458         // this has strange effects when using with 
22459         // top toolbar - not sure if it's a great idea.
22460         //this.editor.contentWindow.focus();
22461         if (typeof sel != "undefined") {
22462             try {
22463                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22464             } catch(e) {
22465                 return this.doc.createRange();
22466             }
22467         } else {
22468             return this.doc.createRange();
22469         }
22470     },
22471     getParentElement: function()
22472     {
22473         
22474         this.assignDocWin();
22475         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22476         
22477         var range = this.createRange(sel);
22478          
22479         try {
22480             var p = range.commonAncestorContainer;
22481             while (p.nodeType == 3) { // text node
22482                 p = p.parentNode;
22483             }
22484             return p;
22485         } catch (e) {
22486             return null;
22487         }
22488     
22489     },
22490     /***
22491      *
22492      * Range intersection.. the hard stuff...
22493      *  '-1' = before
22494      *  '0' = hits..
22495      *  '1' = after.
22496      *         [ -- selected range --- ]
22497      *   [fail]                        [fail]
22498      *
22499      *    basically..
22500      *      if end is before start or  hits it. fail.
22501      *      if start is after end or hits it fail.
22502      *
22503      *   if either hits (but other is outside. - then it's not 
22504      *   
22505      *    
22506      **/
22507     
22508     
22509     // @see http://www.thismuchiknow.co.uk/?p=64.
22510     rangeIntersectsNode : function(range, node)
22511     {
22512         var nodeRange = node.ownerDocument.createRange();
22513         try {
22514             nodeRange.selectNode(node);
22515         } catch (e) {
22516             nodeRange.selectNodeContents(node);
22517         }
22518     
22519         var rangeStartRange = range.cloneRange();
22520         rangeStartRange.collapse(true);
22521     
22522         var rangeEndRange = range.cloneRange();
22523         rangeEndRange.collapse(false);
22524     
22525         var nodeStartRange = nodeRange.cloneRange();
22526         nodeStartRange.collapse(true);
22527     
22528         var nodeEndRange = nodeRange.cloneRange();
22529         nodeEndRange.collapse(false);
22530     
22531         return rangeStartRange.compareBoundaryPoints(
22532                  Range.START_TO_START, nodeEndRange) == -1 &&
22533                rangeEndRange.compareBoundaryPoints(
22534                  Range.START_TO_START, nodeStartRange) == 1;
22535         
22536          
22537     },
22538     rangeCompareNode : function(range, node)
22539     {
22540         var nodeRange = node.ownerDocument.createRange();
22541         try {
22542             nodeRange.selectNode(node);
22543         } catch (e) {
22544             nodeRange.selectNodeContents(node);
22545         }
22546         
22547         
22548         range.collapse(true);
22549     
22550         nodeRange.collapse(true);
22551      
22552         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22553         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22554          
22555         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22556         
22557         var nodeIsBefore   =  ss == 1;
22558         var nodeIsAfter    = ee == -1;
22559         
22560         if (nodeIsBefore && nodeIsAfter) {
22561             return 0; // outer
22562         }
22563         if (!nodeIsBefore && nodeIsAfter) {
22564             return 1; //right trailed.
22565         }
22566         
22567         if (nodeIsBefore && !nodeIsAfter) {
22568             return 2;  // left trailed.
22569         }
22570         // fully contined.
22571         return 3;
22572     },
22573
22574     // private? - in a new class?
22575     cleanUpPaste :  function()
22576     {
22577         // cleans up the whole document..
22578         Roo.log('cleanuppaste');
22579         
22580         this.cleanUpChildren(this.doc.body);
22581         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22582         if (clean != this.doc.body.innerHTML) {
22583             this.doc.body.innerHTML = clean;
22584         }
22585         
22586     },
22587     
22588     cleanWordChars : function(input) {// change the chars to hex code
22589         var he = Roo.HtmlEditorCore;
22590         
22591         var output = input;
22592         Roo.each(he.swapCodes, function(sw) { 
22593             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22594             
22595             output = output.replace(swapper, sw[1]);
22596         });
22597         
22598         return output;
22599     },
22600     
22601     
22602     cleanUpChildren : function (n)
22603     {
22604         if (!n.childNodes.length) {
22605             return;
22606         }
22607         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22608            this.cleanUpChild(n.childNodes[i]);
22609         }
22610     },
22611     
22612     
22613         
22614     
22615     cleanUpChild : function (node)
22616     {
22617         var ed = this;
22618         //console.log(node);
22619         if (node.nodeName == "#text") {
22620             // clean up silly Windows -- stuff?
22621             return; 
22622         }
22623         if (node.nodeName == "#comment") {
22624             node.parentNode.removeChild(node);
22625             // clean up silly Windows -- stuff?
22626             return; 
22627         }
22628         var lcname = node.tagName.toLowerCase();
22629         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22630         // whitelist of tags..
22631         
22632         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22633             // remove node.
22634             node.parentNode.removeChild(node);
22635             return;
22636             
22637         }
22638         
22639         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22640         
22641         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22642         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22643         
22644         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22645         //    remove_keep_children = true;
22646         //}
22647         
22648         if (remove_keep_children) {
22649             this.cleanUpChildren(node);
22650             // inserts everything just before this node...
22651             while (node.childNodes.length) {
22652                 var cn = node.childNodes[0];
22653                 node.removeChild(cn);
22654                 node.parentNode.insertBefore(cn, node);
22655             }
22656             node.parentNode.removeChild(node);
22657             return;
22658         }
22659         
22660         if (!node.attributes || !node.attributes.length) {
22661             this.cleanUpChildren(node);
22662             return;
22663         }
22664         
22665         function cleanAttr(n,v)
22666         {
22667             
22668             if (v.match(/^\./) || v.match(/^\//)) {
22669                 return;
22670             }
22671             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22672                 return;
22673             }
22674             if (v.match(/^#/)) {
22675                 return;
22676             }
22677 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22678             node.removeAttribute(n);
22679             
22680         }
22681         
22682         var cwhite = this.cwhite;
22683         var cblack = this.cblack;
22684             
22685         function cleanStyle(n,v)
22686         {
22687             if (v.match(/expression/)) { //XSS?? should we even bother..
22688                 node.removeAttribute(n);
22689                 return;
22690             }
22691             
22692             var parts = v.split(/;/);
22693             var clean = [];
22694             
22695             Roo.each(parts, function(p) {
22696                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22697                 if (!p.length) {
22698                     return true;
22699                 }
22700                 var l = p.split(':').shift().replace(/\s+/g,'');
22701                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22702                 
22703                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22704 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22705                     //node.removeAttribute(n);
22706                     return true;
22707                 }
22708                 //Roo.log()
22709                 // only allow 'c whitelisted system attributes'
22710                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22711 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22712                     //node.removeAttribute(n);
22713                     return true;
22714                 }
22715                 
22716                 
22717                  
22718                 
22719                 clean.push(p);
22720                 return true;
22721             });
22722             if (clean.length) { 
22723                 node.setAttribute(n, clean.join(';'));
22724             } else {
22725                 node.removeAttribute(n);
22726             }
22727             
22728         }
22729         
22730         
22731         for (var i = node.attributes.length-1; i > -1 ; i--) {
22732             var a = node.attributes[i];
22733             //console.log(a);
22734             
22735             if (a.name.toLowerCase().substr(0,2)=='on')  {
22736                 node.removeAttribute(a.name);
22737                 continue;
22738             }
22739             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22740                 node.removeAttribute(a.name);
22741                 continue;
22742             }
22743             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22744                 cleanAttr(a.name,a.value); // fixme..
22745                 continue;
22746             }
22747             if (a.name == 'style') {
22748                 cleanStyle(a.name,a.value);
22749                 continue;
22750             }
22751             /// clean up MS crap..
22752             // tecnically this should be a list of valid class'es..
22753             
22754             
22755             if (a.name == 'class') {
22756                 if (a.value.match(/^Mso/)) {
22757                     node.className = '';
22758                 }
22759                 
22760                 if (a.value.match(/^body$/)) {
22761                     node.className = '';
22762                 }
22763                 continue;
22764             }
22765             
22766             // style cleanup!?
22767             // class cleanup?
22768             
22769         }
22770         
22771         
22772         this.cleanUpChildren(node);
22773         
22774         
22775     },
22776     
22777     /**
22778      * Clean up MS wordisms...
22779      */
22780     cleanWord : function(node)
22781     {
22782         
22783         
22784         if (!node) {
22785             this.cleanWord(this.doc.body);
22786             return;
22787         }
22788         if (node.nodeName == "#text") {
22789             // clean up silly Windows -- stuff?
22790             return; 
22791         }
22792         if (node.nodeName == "#comment") {
22793             node.parentNode.removeChild(node);
22794             // clean up silly Windows -- stuff?
22795             return; 
22796         }
22797         
22798         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22799             node.parentNode.removeChild(node);
22800             return;
22801         }
22802         
22803         // remove - but keep children..
22804         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22805             while (node.childNodes.length) {
22806                 var cn = node.childNodes[0];
22807                 node.removeChild(cn);
22808                 node.parentNode.insertBefore(cn, node);
22809             }
22810             node.parentNode.removeChild(node);
22811             this.iterateChildren(node, this.cleanWord);
22812             return;
22813         }
22814         // clean styles
22815         if (node.className.length) {
22816             
22817             var cn = node.className.split(/\W+/);
22818             var cna = [];
22819             Roo.each(cn, function(cls) {
22820                 if (cls.match(/Mso[a-zA-Z]+/)) {
22821                     return;
22822                 }
22823                 cna.push(cls);
22824             });
22825             node.className = cna.length ? cna.join(' ') : '';
22826             if (!cna.length) {
22827                 node.removeAttribute("class");
22828             }
22829         }
22830         
22831         if (node.hasAttribute("lang")) {
22832             node.removeAttribute("lang");
22833         }
22834         
22835         if (node.hasAttribute("style")) {
22836             
22837             var styles = node.getAttribute("style").split(";");
22838             var nstyle = [];
22839             Roo.each(styles, function(s) {
22840                 if (!s.match(/:/)) {
22841                     return;
22842                 }
22843                 var kv = s.split(":");
22844                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22845                     return;
22846                 }
22847                 // what ever is left... we allow.
22848                 nstyle.push(s);
22849             });
22850             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22851             if (!nstyle.length) {
22852                 node.removeAttribute('style');
22853             }
22854         }
22855         this.iterateChildren(node, this.cleanWord);
22856         
22857         
22858         
22859     },
22860     /**
22861      * iterateChildren of a Node, calling fn each time, using this as the scole..
22862      * @param {DomNode} node node to iterate children of.
22863      * @param {Function} fn method of this class to call on each item.
22864      */
22865     iterateChildren : function(node, fn)
22866     {
22867         if (!node.childNodes.length) {
22868                 return;
22869         }
22870         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22871            fn.call(this, node.childNodes[i])
22872         }
22873     },
22874     
22875     
22876     /**
22877      * cleanTableWidths.
22878      *
22879      * Quite often pasting from word etc.. results in tables with column and widths.
22880      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22881      *
22882      */
22883     cleanTableWidths : function(node)
22884     {
22885          
22886          
22887         if (!node) {
22888             this.cleanTableWidths(this.doc.body);
22889             return;
22890         }
22891         
22892         // ignore list...
22893         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22894             return; 
22895         }
22896         Roo.log(node.tagName);
22897         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22898             this.iterateChildren(node, this.cleanTableWidths);
22899             return;
22900         }
22901         if (node.hasAttribute('width')) {
22902             node.removeAttribute('width');
22903         }
22904         
22905          
22906         if (node.hasAttribute("style")) {
22907             // pretty basic...
22908             
22909             var styles = node.getAttribute("style").split(";");
22910             var nstyle = [];
22911             Roo.each(styles, function(s) {
22912                 if (!s.match(/:/)) {
22913                     return;
22914                 }
22915                 var kv = s.split(":");
22916                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22917                     return;
22918                 }
22919                 // what ever is left... we allow.
22920                 nstyle.push(s);
22921             });
22922             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22923             if (!nstyle.length) {
22924                 node.removeAttribute('style');
22925             }
22926         }
22927         
22928         this.iterateChildren(node, this.cleanTableWidths);
22929         
22930         
22931     },
22932     
22933     
22934     
22935     
22936     domToHTML : function(currentElement, depth, nopadtext) {
22937         
22938         depth = depth || 0;
22939         nopadtext = nopadtext || false;
22940     
22941         if (!currentElement) {
22942             return this.domToHTML(this.doc.body);
22943         }
22944         
22945         //Roo.log(currentElement);
22946         var j;
22947         var allText = false;
22948         var nodeName = currentElement.nodeName;
22949         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22950         
22951         if  (nodeName == '#text') {
22952             
22953             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22954         }
22955         
22956         
22957         var ret = '';
22958         if (nodeName != 'BODY') {
22959              
22960             var i = 0;
22961             // Prints the node tagName, such as <A>, <IMG>, etc
22962             if (tagName) {
22963                 var attr = [];
22964                 for(i = 0; i < currentElement.attributes.length;i++) {
22965                     // quoting?
22966                     var aname = currentElement.attributes.item(i).name;
22967                     if (!currentElement.attributes.item(i).value.length) {
22968                         continue;
22969                     }
22970                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22971                 }
22972                 
22973                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22974             } 
22975             else {
22976                 
22977                 // eack
22978             }
22979         } else {
22980             tagName = false;
22981         }
22982         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22983             return ret;
22984         }
22985         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22986             nopadtext = true;
22987         }
22988         
22989         
22990         // Traverse the tree
22991         i = 0;
22992         var currentElementChild = currentElement.childNodes.item(i);
22993         var allText = true;
22994         var innerHTML  = '';
22995         lastnode = '';
22996         while (currentElementChild) {
22997             // Formatting code (indent the tree so it looks nice on the screen)
22998             var nopad = nopadtext;
22999             if (lastnode == 'SPAN') {
23000                 nopad  = true;
23001             }
23002             // text
23003             if  (currentElementChild.nodeName == '#text') {
23004                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23005                 toadd = nopadtext ? toadd : toadd.trim();
23006                 if (!nopad && toadd.length > 80) {
23007                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23008                 }
23009                 innerHTML  += toadd;
23010                 
23011                 i++;
23012                 currentElementChild = currentElement.childNodes.item(i);
23013                 lastNode = '';
23014                 continue;
23015             }
23016             allText = false;
23017             
23018             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23019                 
23020             // Recursively traverse the tree structure of the child node
23021             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23022             lastnode = currentElementChild.nodeName;
23023             i++;
23024             currentElementChild=currentElement.childNodes.item(i);
23025         }
23026         
23027         ret += innerHTML;
23028         
23029         if (!allText) {
23030                 // The remaining code is mostly for formatting the tree
23031             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23032         }
23033         
23034         
23035         if (tagName) {
23036             ret+= "</"+tagName+">";
23037         }
23038         return ret;
23039         
23040     },
23041         
23042     applyBlacklists : function()
23043     {
23044         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23045         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23046         
23047         this.white = [];
23048         this.black = [];
23049         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23050             if (b.indexOf(tag) > -1) {
23051                 return;
23052             }
23053             this.white.push(tag);
23054             
23055         }, this);
23056         
23057         Roo.each(w, function(tag) {
23058             if (b.indexOf(tag) > -1) {
23059                 return;
23060             }
23061             if (this.white.indexOf(tag) > -1) {
23062                 return;
23063             }
23064             this.white.push(tag);
23065             
23066         }, this);
23067         
23068         
23069         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23070             if (w.indexOf(tag) > -1) {
23071                 return;
23072             }
23073             this.black.push(tag);
23074             
23075         }, this);
23076         
23077         Roo.each(b, function(tag) {
23078             if (w.indexOf(tag) > -1) {
23079                 return;
23080             }
23081             if (this.black.indexOf(tag) > -1) {
23082                 return;
23083             }
23084             this.black.push(tag);
23085             
23086         }, this);
23087         
23088         
23089         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23090         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23091         
23092         this.cwhite = [];
23093         this.cblack = [];
23094         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23095             if (b.indexOf(tag) > -1) {
23096                 return;
23097             }
23098             this.cwhite.push(tag);
23099             
23100         }, this);
23101         
23102         Roo.each(w, function(tag) {
23103             if (b.indexOf(tag) > -1) {
23104                 return;
23105             }
23106             if (this.cwhite.indexOf(tag) > -1) {
23107                 return;
23108             }
23109             this.cwhite.push(tag);
23110             
23111         }, this);
23112         
23113         
23114         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23115             if (w.indexOf(tag) > -1) {
23116                 return;
23117             }
23118             this.cblack.push(tag);
23119             
23120         }, this);
23121         
23122         Roo.each(b, function(tag) {
23123             if (w.indexOf(tag) > -1) {
23124                 return;
23125             }
23126             if (this.cblack.indexOf(tag) > -1) {
23127                 return;
23128             }
23129             this.cblack.push(tag);
23130             
23131         }, this);
23132     },
23133     
23134     setStylesheets : function(stylesheets)
23135     {
23136         if(typeof(stylesheets) == 'string'){
23137             Roo.get(this.iframe.contentDocument.head).createChild({
23138                 tag : 'link',
23139                 rel : 'stylesheet',
23140                 type : 'text/css',
23141                 href : stylesheets
23142             });
23143             
23144             return;
23145         }
23146         var _this = this;
23147      
23148         Roo.each(stylesheets, function(s) {
23149             if(!s.length){
23150                 return;
23151             }
23152             
23153             Roo.get(_this.iframe.contentDocument.head).createChild({
23154                 tag : 'link',
23155                 rel : 'stylesheet',
23156                 type : 'text/css',
23157                 href : s
23158             });
23159         });
23160
23161         
23162     },
23163     
23164     removeStylesheets : function()
23165     {
23166         var _this = this;
23167         
23168         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23169             s.remove();
23170         });
23171     },
23172     
23173     setStyle : function(style)
23174     {
23175         Roo.get(this.iframe.contentDocument.head).createChild({
23176             tag : 'style',
23177             type : 'text/css',
23178             html : style
23179         });
23180
23181         return;
23182     }
23183     
23184     // hide stuff that is not compatible
23185     /**
23186      * @event blur
23187      * @hide
23188      */
23189     /**
23190      * @event change
23191      * @hide
23192      */
23193     /**
23194      * @event focus
23195      * @hide
23196      */
23197     /**
23198      * @event specialkey
23199      * @hide
23200      */
23201     /**
23202      * @cfg {String} fieldClass @hide
23203      */
23204     /**
23205      * @cfg {String} focusClass @hide
23206      */
23207     /**
23208      * @cfg {String} autoCreate @hide
23209      */
23210     /**
23211      * @cfg {String} inputType @hide
23212      */
23213     /**
23214      * @cfg {String} invalidClass @hide
23215      */
23216     /**
23217      * @cfg {String} invalidText @hide
23218      */
23219     /**
23220      * @cfg {String} msgFx @hide
23221      */
23222     /**
23223      * @cfg {String} validateOnBlur @hide
23224      */
23225 });
23226
23227 Roo.HtmlEditorCore.white = [
23228         'area', 'br', 'img', 'input', 'hr', 'wbr',
23229         
23230        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23231        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23232        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23233        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23234        'table',   'ul',         'xmp', 
23235        
23236        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23237       'thead',   'tr', 
23238      
23239       'dir', 'menu', 'ol', 'ul', 'dl',
23240        
23241       'embed',  'object'
23242 ];
23243
23244
23245 Roo.HtmlEditorCore.black = [
23246     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23247         'applet', // 
23248         'base',   'basefont', 'bgsound', 'blink',  'body', 
23249         'frame',  'frameset', 'head',    'html',   'ilayer', 
23250         'iframe', 'layer',  'link',     'meta',    'object',   
23251         'script', 'style' ,'title',  'xml' // clean later..
23252 ];
23253 Roo.HtmlEditorCore.clean = [
23254     'script', 'style', 'title', 'xml'
23255 ];
23256 Roo.HtmlEditorCore.remove = [
23257     'font'
23258 ];
23259 // attributes..
23260
23261 Roo.HtmlEditorCore.ablack = [
23262     'on'
23263 ];
23264     
23265 Roo.HtmlEditorCore.aclean = [ 
23266     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23267 ];
23268
23269 // protocols..
23270 Roo.HtmlEditorCore.pwhite= [
23271         'http',  'https',  'mailto'
23272 ];
23273
23274 // white listed style attributes.
23275 Roo.HtmlEditorCore.cwhite= [
23276       //  'text-align', /// default is to allow most things..
23277       
23278          
23279 //        'font-size'//??
23280 ];
23281
23282 // black listed style attributes.
23283 Roo.HtmlEditorCore.cblack= [
23284       //  'font-size' -- this can be set by the project 
23285 ];
23286
23287
23288 Roo.HtmlEditorCore.swapCodes   =[ 
23289     [    8211, "--" ], 
23290     [    8212, "--" ], 
23291     [    8216,  "'" ],  
23292     [    8217, "'" ],  
23293     [    8220, '"' ],  
23294     [    8221, '"' ],  
23295     [    8226, "*" ],  
23296     [    8230, "..." ]
23297 ]; 
23298
23299     /*
23300  * - LGPL
23301  *
23302  * HtmlEditor
23303  * 
23304  */
23305
23306 /**
23307  * @class Roo.bootstrap.HtmlEditor
23308  * @extends Roo.bootstrap.TextArea
23309  * Bootstrap HtmlEditor class
23310
23311  * @constructor
23312  * Create a new HtmlEditor
23313  * @param {Object} config The config object
23314  */
23315
23316 Roo.bootstrap.HtmlEditor = function(config){
23317     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23318     if (!this.toolbars) {
23319         this.toolbars = [];
23320     }
23321     
23322     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23323     this.addEvents({
23324             /**
23325              * @event initialize
23326              * Fires when the editor is fully initialized (including the iframe)
23327              * @param {HtmlEditor} this
23328              */
23329             initialize: true,
23330             /**
23331              * @event activate
23332              * Fires when the editor is first receives the focus. Any insertion must wait
23333              * until after this event.
23334              * @param {HtmlEditor} this
23335              */
23336             activate: true,
23337              /**
23338              * @event beforesync
23339              * Fires before the textarea is updated with content from the editor iframe. Return false
23340              * to cancel the sync.
23341              * @param {HtmlEditor} this
23342              * @param {String} html
23343              */
23344             beforesync: true,
23345              /**
23346              * @event beforepush
23347              * Fires before the iframe editor is updated with content from the textarea. Return false
23348              * to cancel the push.
23349              * @param {HtmlEditor} this
23350              * @param {String} html
23351              */
23352             beforepush: true,
23353              /**
23354              * @event sync
23355              * Fires when the textarea is updated with content from the editor iframe.
23356              * @param {HtmlEditor} this
23357              * @param {String} html
23358              */
23359             sync: true,
23360              /**
23361              * @event push
23362              * Fires when the iframe editor is updated with content from the textarea.
23363              * @param {HtmlEditor} this
23364              * @param {String} html
23365              */
23366             push: true,
23367              /**
23368              * @event editmodechange
23369              * Fires when the editor switches edit modes
23370              * @param {HtmlEditor} this
23371              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23372              */
23373             editmodechange: true,
23374             /**
23375              * @event editorevent
23376              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23377              * @param {HtmlEditor} this
23378              */
23379             editorevent: true,
23380             /**
23381              * @event firstfocus
23382              * Fires when on first focus - needed by toolbars..
23383              * @param {HtmlEditor} this
23384              */
23385             firstfocus: true,
23386             /**
23387              * @event autosave
23388              * Auto save the htmlEditor value as a file into Events
23389              * @param {HtmlEditor} this
23390              */
23391             autosave: true,
23392             /**
23393              * @event savedpreview
23394              * preview the saved version of htmlEditor
23395              * @param {HtmlEditor} this
23396              */
23397             savedpreview: true
23398         });
23399 };
23400
23401
23402 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23403     
23404     
23405       /**
23406      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23407      */
23408     toolbars : false,
23409     
23410      /**
23411     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23412     */
23413     btns : [],
23414    
23415      /**
23416      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23417      *                        Roo.resizable.
23418      */
23419     resizable : false,
23420      /**
23421      * @cfg {Number} height (in pixels)
23422      */   
23423     height: 300,
23424    /**
23425      * @cfg {Number} width (in pixels)
23426      */   
23427     width: false,
23428     
23429     /**
23430      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23431      * 
23432      */
23433     stylesheets: false,
23434     
23435     // id of frame..
23436     frameId: false,
23437     
23438     // private properties
23439     validationEvent : false,
23440     deferHeight: true,
23441     initialized : false,
23442     activated : false,
23443     
23444     onFocus : Roo.emptyFn,
23445     iframePad:3,
23446     hideMode:'offsets',
23447     
23448     tbContainer : false,
23449     
23450     bodyCls : '',
23451     
23452     toolbarContainer :function() {
23453         return this.wrap.select('.x-html-editor-tb',true).first();
23454     },
23455
23456     /**
23457      * Protected method that will not generally be called directly. It
23458      * is called when the editor creates its toolbar. Override this method if you need to
23459      * add custom toolbar buttons.
23460      * @param {HtmlEditor} editor
23461      */
23462     createToolbar : function(){
23463         Roo.log('renewing');
23464         Roo.log("create toolbars");
23465         
23466         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23467         this.toolbars[0].render(this.toolbarContainer());
23468         
23469         return;
23470         
23471 //        if (!editor.toolbars || !editor.toolbars.length) {
23472 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23473 //        }
23474 //        
23475 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23476 //            editor.toolbars[i] = Roo.factory(
23477 //                    typeof(editor.toolbars[i]) == 'string' ?
23478 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23479 //                Roo.bootstrap.HtmlEditor);
23480 //            editor.toolbars[i].init(editor);
23481 //        }
23482     },
23483
23484      
23485     // private
23486     onRender : function(ct, position)
23487     {
23488        // Roo.log("Call onRender: " + this.xtype);
23489         var _t = this;
23490         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23491       
23492         this.wrap = this.inputEl().wrap({
23493             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23494         });
23495         
23496         this.editorcore.onRender(ct, position);
23497          
23498         if (this.resizable) {
23499             this.resizeEl = new Roo.Resizable(this.wrap, {
23500                 pinned : true,
23501                 wrap: true,
23502                 dynamic : true,
23503                 minHeight : this.height,
23504                 height: this.height,
23505                 handles : this.resizable,
23506                 width: this.width,
23507                 listeners : {
23508                     resize : function(r, w, h) {
23509                         _t.onResize(w,h); // -something
23510                     }
23511                 }
23512             });
23513             
23514         }
23515         this.createToolbar(this);
23516        
23517         
23518         if(!this.width && this.resizable){
23519             this.setSize(this.wrap.getSize());
23520         }
23521         if (this.resizeEl) {
23522             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23523             // should trigger onReize..
23524         }
23525         
23526     },
23527
23528     // private
23529     onResize : function(w, h)
23530     {
23531         Roo.log('resize: ' +w + ',' + h );
23532         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23533         var ew = false;
23534         var eh = false;
23535         
23536         if(this.inputEl() ){
23537             if(typeof w == 'number'){
23538                 var aw = w - this.wrap.getFrameWidth('lr');
23539                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23540                 ew = aw;
23541             }
23542             if(typeof h == 'number'){
23543                  var tbh = -11;  // fixme it needs to tool bar size!
23544                 for (var i =0; i < this.toolbars.length;i++) {
23545                     // fixme - ask toolbars for heights?
23546                     tbh += this.toolbars[i].el.getHeight();
23547                     //if (this.toolbars[i].footer) {
23548                     //    tbh += this.toolbars[i].footer.el.getHeight();
23549                     //}
23550                 }
23551               
23552                 
23553                 
23554                 
23555                 
23556                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23557                 ah -= 5; // knock a few pixes off for look..
23558                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23559                 var eh = ah;
23560             }
23561         }
23562         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23563         this.editorcore.onResize(ew,eh);
23564         
23565     },
23566
23567     /**
23568      * Toggles the editor between standard and source edit mode.
23569      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23570      */
23571     toggleSourceEdit : function(sourceEditMode)
23572     {
23573         this.editorcore.toggleSourceEdit(sourceEditMode);
23574         
23575         if(this.editorcore.sourceEditMode){
23576             Roo.log('editor - showing textarea');
23577             
23578 //            Roo.log('in');
23579 //            Roo.log(this.syncValue());
23580             this.syncValue();
23581             this.inputEl().removeClass(['hide', 'x-hidden']);
23582             this.inputEl().dom.removeAttribute('tabIndex');
23583             this.inputEl().focus();
23584         }else{
23585             Roo.log('editor - hiding textarea');
23586 //            Roo.log('out')
23587 //            Roo.log(this.pushValue()); 
23588             this.pushValue();
23589             
23590             this.inputEl().addClass(['hide', 'x-hidden']);
23591             this.inputEl().dom.setAttribute('tabIndex', -1);
23592             //this.deferFocus();
23593         }
23594          
23595         if(this.resizable){
23596             this.setSize(this.wrap.getSize());
23597         }
23598         
23599         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23600     },
23601  
23602     // private (for BoxComponent)
23603     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23604
23605     // private (for BoxComponent)
23606     getResizeEl : function(){
23607         return this.wrap;
23608     },
23609
23610     // private (for BoxComponent)
23611     getPositionEl : function(){
23612         return this.wrap;
23613     },
23614
23615     // private
23616     initEvents : function(){
23617         this.originalValue = this.getValue();
23618     },
23619
23620 //    /**
23621 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23622 //     * @method
23623 //     */
23624 //    markInvalid : Roo.emptyFn,
23625 //    /**
23626 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23627 //     * @method
23628 //     */
23629 //    clearInvalid : Roo.emptyFn,
23630
23631     setValue : function(v){
23632         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23633         this.editorcore.pushValue();
23634     },
23635
23636      
23637     // private
23638     deferFocus : function(){
23639         this.focus.defer(10, this);
23640     },
23641
23642     // doc'ed in Field
23643     focus : function(){
23644         this.editorcore.focus();
23645         
23646     },
23647       
23648
23649     // private
23650     onDestroy : function(){
23651         
23652         
23653         
23654         if(this.rendered){
23655             
23656             for (var i =0; i < this.toolbars.length;i++) {
23657                 // fixme - ask toolbars for heights?
23658                 this.toolbars[i].onDestroy();
23659             }
23660             
23661             this.wrap.dom.innerHTML = '';
23662             this.wrap.remove();
23663         }
23664     },
23665
23666     // private
23667     onFirstFocus : function(){
23668         //Roo.log("onFirstFocus");
23669         this.editorcore.onFirstFocus();
23670          for (var i =0; i < this.toolbars.length;i++) {
23671             this.toolbars[i].onFirstFocus();
23672         }
23673         
23674     },
23675     
23676     // private
23677     syncValue : function()
23678     {   
23679         this.editorcore.syncValue();
23680     },
23681     
23682     pushValue : function()
23683     {   
23684         this.editorcore.pushValue();
23685     }
23686      
23687     
23688     // hide stuff that is not compatible
23689     /**
23690      * @event blur
23691      * @hide
23692      */
23693     /**
23694      * @event change
23695      * @hide
23696      */
23697     /**
23698      * @event focus
23699      * @hide
23700      */
23701     /**
23702      * @event specialkey
23703      * @hide
23704      */
23705     /**
23706      * @cfg {String} fieldClass @hide
23707      */
23708     /**
23709      * @cfg {String} focusClass @hide
23710      */
23711     /**
23712      * @cfg {String} autoCreate @hide
23713      */
23714     /**
23715      * @cfg {String} inputType @hide
23716      */
23717     /**
23718      * @cfg {String} invalidClass @hide
23719      */
23720     /**
23721      * @cfg {String} invalidText @hide
23722      */
23723     /**
23724      * @cfg {String} msgFx @hide
23725      */
23726     /**
23727      * @cfg {String} validateOnBlur @hide
23728      */
23729 });
23730  
23731     
23732    
23733    
23734    
23735       
23736 Roo.namespace('Roo.bootstrap.htmleditor');
23737 /**
23738  * @class Roo.bootstrap.HtmlEditorToolbar1
23739  * Basic Toolbar
23740  * 
23741  * Usage:
23742  *
23743  new Roo.bootstrap.HtmlEditor({
23744     ....
23745     toolbars : [
23746         new Roo.bootstrap.HtmlEditorToolbar1({
23747             disable : { fonts: 1 , format: 1, ..., ... , ...],
23748             btns : [ .... ]
23749         })
23750     }
23751      
23752  * 
23753  * @cfg {Object} disable List of elements to disable..
23754  * @cfg {Array} btns List of additional buttons.
23755  * 
23756  * 
23757  * NEEDS Extra CSS? 
23758  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23759  */
23760  
23761 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23762 {
23763     
23764     Roo.apply(this, config);
23765     
23766     // default disabled, based on 'good practice'..
23767     this.disable = this.disable || {};
23768     Roo.applyIf(this.disable, {
23769         fontSize : true,
23770         colors : true,
23771         specialElements : true
23772     });
23773     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23774     
23775     this.editor = config.editor;
23776     this.editorcore = config.editor.editorcore;
23777     
23778     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23779     
23780     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23781     // dont call parent... till later.
23782 }
23783 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23784      
23785     bar : true,
23786     
23787     editor : false,
23788     editorcore : false,
23789     
23790     
23791     formats : [
23792         "p" ,  
23793         "h1","h2","h3","h4","h5","h6", 
23794         "pre", "code", 
23795         "abbr", "acronym", "address", "cite", "samp", "var",
23796         'div','span'
23797     ],
23798     
23799     onRender : function(ct, position)
23800     {
23801        // Roo.log("Call onRender: " + this.xtype);
23802         
23803        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23804        Roo.log(this.el);
23805        this.el.dom.style.marginBottom = '0';
23806        var _this = this;
23807        var editorcore = this.editorcore;
23808        var editor= this.editor;
23809        
23810        var children = [];
23811        var btn = function(id,cmd , toggle, handler, html){
23812        
23813             var  event = toggle ? 'toggle' : 'click';
23814        
23815             var a = {
23816                 size : 'sm',
23817                 xtype: 'Button',
23818                 xns: Roo.bootstrap,
23819                 glyphicon : id,
23820                 cmd : id || cmd,
23821                 enableToggle:toggle !== false,
23822                 html : html || '',
23823                 pressed : toggle ? false : null,
23824                 listeners : {}
23825             };
23826             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23827                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23828             };
23829             children.push(a);
23830             return a;
23831        }
23832        
23833     //    var cb_box = function...
23834         
23835         var style = {
23836                 xtype: 'Button',
23837                 size : 'sm',
23838                 xns: Roo.bootstrap,
23839                 glyphicon : 'font',
23840                 //html : 'submit'
23841                 menu : {
23842                     xtype: 'Menu',
23843                     xns: Roo.bootstrap,
23844                     items:  []
23845                 }
23846         };
23847         Roo.each(this.formats, function(f) {
23848             style.menu.items.push({
23849                 xtype :'MenuItem',
23850                 xns: Roo.bootstrap,
23851                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23852                 tagname : f,
23853                 listeners : {
23854                     click : function()
23855                     {
23856                         editorcore.insertTag(this.tagname);
23857                         editor.focus();
23858                     }
23859                 }
23860                 
23861             });
23862         });
23863         children.push(style);   
23864         
23865         btn('bold',false,true);
23866         btn('italic',false,true);
23867         btn('align-left', 'justifyleft',true);
23868         btn('align-center', 'justifycenter',true);
23869         btn('align-right' , 'justifyright',true);
23870         btn('link', false, false, function(btn) {
23871             //Roo.log("create link?");
23872             var url = prompt(this.createLinkText, this.defaultLinkValue);
23873             if(url && url != 'http:/'+'/'){
23874                 this.editorcore.relayCmd('createlink', url);
23875             }
23876         }),
23877         btn('list','insertunorderedlist',true);
23878         btn('pencil', false,true, function(btn){
23879                 Roo.log(this);
23880                 this.toggleSourceEdit(btn.pressed);
23881         });
23882         
23883         if (this.editor.btns.length > 0) {
23884             for (var i = 0; i<this.editor.btns.length; i++) {
23885                 children.push(this.editor.btns[i]);
23886             }
23887         }
23888         
23889         /*
23890         var cog = {
23891                 xtype: 'Button',
23892                 size : 'sm',
23893                 xns: Roo.bootstrap,
23894                 glyphicon : 'cog',
23895                 //html : 'submit'
23896                 menu : {
23897                     xtype: 'Menu',
23898                     xns: Roo.bootstrap,
23899                     items:  []
23900                 }
23901         };
23902         
23903         cog.menu.items.push({
23904             xtype :'MenuItem',
23905             xns: Roo.bootstrap,
23906             html : Clean styles,
23907             tagname : f,
23908             listeners : {
23909                 click : function()
23910                 {
23911                     editorcore.insertTag(this.tagname);
23912                     editor.focus();
23913                 }
23914             }
23915             
23916         });
23917        */
23918         
23919          
23920        this.xtype = 'NavSimplebar';
23921         
23922         for(var i=0;i< children.length;i++) {
23923             
23924             this.buttons.add(this.addxtypeChild(children[i]));
23925             
23926         }
23927         
23928         editor.on('editorevent', this.updateToolbar, this);
23929     },
23930     onBtnClick : function(id)
23931     {
23932        this.editorcore.relayCmd(id);
23933        this.editorcore.focus();
23934     },
23935     
23936     /**
23937      * Protected method that will not generally be called directly. It triggers
23938      * a toolbar update by reading the markup state of the current selection in the editor.
23939      */
23940     updateToolbar: function(){
23941
23942         if(!this.editorcore.activated){
23943             this.editor.onFirstFocus(); // is this neeed?
23944             return;
23945         }
23946
23947         var btns = this.buttons; 
23948         var doc = this.editorcore.doc;
23949         btns.get('bold').setActive(doc.queryCommandState('bold'));
23950         btns.get('italic').setActive(doc.queryCommandState('italic'));
23951         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23952         
23953         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23954         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23955         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23956         
23957         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23958         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23959          /*
23960         
23961         var ans = this.editorcore.getAllAncestors();
23962         if (this.formatCombo) {
23963             
23964             
23965             var store = this.formatCombo.store;
23966             this.formatCombo.setValue("");
23967             for (var i =0; i < ans.length;i++) {
23968                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23969                     // select it..
23970                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23971                     break;
23972                 }
23973             }
23974         }
23975         
23976         
23977         
23978         // hides menus... - so this cant be on a menu...
23979         Roo.bootstrap.MenuMgr.hideAll();
23980         */
23981         Roo.bootstrap.MenuMgr.hideAll();
23982         //this.editorsyncValue();
23983     },
23984     onFirstFocus: function() {
23985         this.buttons.each(function(item){
23986            item.enable();
23987         });
23988     },
23989     toggleSourceEdit : function(sourceEditMode){
23990         
23991           
23992         if(sourceEditMode){
23993             Roo.log("disabling buttons");
23994            this.buttons.each( function(item){
23995                 if(item.cmd != 'pencil'){
23996                     item.disable();
23997                 }
23998             });
23999           
24000         }else{
24001             Roo.log("enabling buttons");
24002             if(this.editorcore.initialized){
24003                 this.buttons.each( function(item){
24004                     item.enable();
24005                 });
24006             }
24007             
24008         }
24009         Roo.log("calling toggole on editor");
24010         // tell the editor that it's been pressed..
24011         this.editor.toggleSourceEdit(sourceEditMode);
24012        
24013     }
24014 });
24015
24016
24017
24018
24019
24020 /**
24021  * @class Roo.bootstrap.Table.AbstractSelectionModel
24022  * @extends Roo.util.Observable
24023  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24024  * implemented by descendant classes.  This class should not be directly instantiated.
24025  * @constructor
24026  */
24027 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24028     this.locked = false;
24029     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24030 };
24031
24032
24033 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24034     /** @ignore Called by the grid automatically. Do not call directly. */
24035     init : function(grid){
24036         this.grid = grid;
24037         this.initEvents();
24038     },
24039
24040     /**
24041      * Locks the selections.
24042      */
24043     lock : function(){
24044         this.locked = true;
24045     },
24046
24047     /**
24048      * Unlocks the selections.
24049      */
24050     unlock : function(){
24051         this.locked = false;
24052     },
24053
24054     /**
24055      * Returns true if the selections are locked.
24056      * @return {Boolean}
24057      */
24058     isLocked : function(){
24059         return this.locked;
24060     }
24061 });
24062 /**
24063  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24064  * @class Roo.bootstrap.Table.RowSelectionModel
24065  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24066  * It supports multiple selections and keyboard selection/navigation. 
24067  * @constructor
24068  * @param {Object} config
24069  */
24070
24071 Roo.bootstrap.Table.RowSelectionModel = function(config){
24072     Roo.apply(this, config);
24073     this.selections = new Roo.util.MixedCollection(false, function(o){
24074         return o.id;
24075     });
24076
24077     this.last = false;
24078     this.lastActive = false;
24079
24080     this.addEvents({
24081         /**
24082              * @event selectionchange
24083              * Fires when the selection changes
24084              * @param {SelectionModel} this
24085              */
24086             "selectionchange" : true,
24087         /**
24088              * @event afterselectionchange
24089              * Fires after the selection changes (eg. by key press or clicking)
24090              * @param {SelectionModel} this
24091              */
24092             "afterselectionchange" : true,
24093         /**
24094              * @event beforerowselect
24095              * Fires when a row is selected being selected, return false to cancel.
24096              * @param {SelectionModel} this
24097              * @param {Number} rowIndex The selected index
24098              * @param {Boolean} keepExisting False if other selections will be cleared
24099              */
24100             "beforerowselect" : true,
24101         /**
24102              * @event rowselect
24103              * Fires when a row is selected.
24104              * @param {SelectionModel} this
24105              * @param {Number} rowIndex The selected index
24106              * @param {Roo.data.Record} r The record
24107              */
24108             "rowselect" : true,
24109         /**
24110              * @event rowdeselect
24111              * Fires when a row is deselected.
24112              * @param {SelectionModel} this
24113              * @param {Number} rowIndex The selected index
24114              */
24115         "rowdeselect" : true
24116     });
24117     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24118     this.locked = false;
24119  };
24120
24121 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24122     /**
24123      * @cfg {Boolean} singleSelect
24124      * True to allow selection of only one row at a time (defaults to false)
24125      */
24126     singleSelect : false,
24127
24128     // private
24129     initEvents : function()
24130     {
24131
24132         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24133         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24134         //}else{ // allow click to work like normal
24135          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24136         //}
24137         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24138         this.grid.on("rowclick", this.handleMouseDown, this);
24139         
24140         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24141             "up" : function(e){
24142                 if(!e.shiftKey){
24143                     this.selectPrevious(e.shiftKey);
24144                 }else if(this.last !== false && this.lastActive !== false){
24145                     var last = this.last;
24146                     this.selectRange(this.last,  this.lastActive-1);
24147                     this.grid.getView().focusRow(this.lastActive);
24148                     if(last !== false){
24149                         this.last = last;
24150                     }
24151                 }else{
24152                     this.selectFirstRow();
24153                 }
24154                 this.fireEvent("afterselectionchange", this);
24155             },
24156             "down" : function(e){
24157                 if(!e.shiftKey){
24158                     this.selectNext(e.shiftKey);
24159                 }else if(this.last !== false && this.lastActive !== false){
24160                     var last = this.last;
24161                     this.selectRange(this.last,  this.lastActive+1);
24162                     this.grid.getView().focusRow(this.lastActive);
24163                     if(last !== false){
24164                         this.last = last;
24165                     }
24166                 }else{
24167                     this.selectFirstRow();
24168                 }
24169                 this.fireEvent("afterselectionchange", this);
24170             },
24171             scope: this
24172         });
24173         this.grid.store.on('load', function(){
24174             this.selections.clear();
24175         },this);
24176         /*
24177         var view = this.grid.view;
24178         view.on("refresh", this.onRefresh, this);
24179         view.on("rowupdated", this.onRowUpdated, this);
24180         view.on("rowremoved", this.onRemove, this);
24181         */
24182     },
24183
24184     // private
24185     onRefresh : function()
24186     {
24187         var ds = this.grid.store, i, v = this.grid.view;
24188         var s = this.selections;
24189         s.each(function(r){
24190             if((i = ds.indexOfId(r.id)) != -1){
24191                 v.onRowSelect(i);
24192             }else{
24193                 s.remove(r);
24194             }
24195         });
24196     },
24197
24198     // private
24199     onRemove : function(v, index, r){
24200         this.selections.remove(r);
24201     },
24202
24203     // private
24204     onRowUpdated : function(v, index, r){
24205         if(this.isSelected(r)){
24206             v.onRowSelect(index);
24207         }
24208     },
24209
24210     /**
24211      * Select records.
24212      * @param {Array} records The records to select
24213      * @param {Boolean} keepExisting (optional) True to keep existing selections
24214      */
24215     selectRecords : function(records, keepExisting)
24216     {
24217         if(!keepExisting){
24218             this.clearSelections();
24219         }
24220             var ds = this.grid.store;
24221         for(var i = 0, len = records.length; i < len; i++){
24222             this.selectRow(ds.indexOf(records[i]), true);
24223         }
24224     },
24225
24226     /**
24227      * Gets the number of selected rows.
24228      * @return {Number}
24229      */
24230     getCount : function(){
24231         return this.selections.length;
24232     },
24233
24234     /**
24235      * Selects the first row in the grid.
24236      */
24237     selectFirstRow : function(){
24238         this.selectRow(0);
24239     },
24240
24241     /**
24242      * Select the last row.
24243      * @param {Boolean} keepExisting (optional) True to keep existing selections
24244      */
24245     selectLastRow : function(keepExisting){
24246         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24247         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24248     },
24249
24250     /**
24251      * Selects the row immediately following the last selected row.
24252      * @param {Boolean} keepExisting (optional) True to keep existing selections
24253      */
24254     selectNext : function(keepExisting)
24255     {
24256             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24257             this.selectRow(this.last+1, keepExisting);
24258             this.grid.getView().focusRow(this.last);
24259         }
24260     },
24261
24262     /**
24263      * Selects the row that precedes the last selected row.
24264      * @param {Boolean} keepExisting (optional) True to keep existing selections
24265      */
24266     selectPrevious : function(keepExisting){
24267         if(this.last){
24268             this.selectRow(this.last-1, keepExisting);
24269             this.grid.getView().focusRow(this.last);
24270         }
24271     },
24272
24273     /**
24274      * Returns the selected records
24275      * @return {Array} Array of selected records
24276      */
24277     getSelections : function(){
24278         return [].concat(this.selections.items);
24279     },
24280
24281     /**
24282      * Returns the first selected record.
24283      * @return {Record}
24284      */
24285     getSelected : function(){
24286         return this.selections.itemAt(0);
24287     },
24288
24289
24290     /**
24291      * Clears all selections.
24292      */
24293     clearSelections : function(fast)
24294     {
24295         if(this.locked) {
24296             return;
24297         }
24298         if(fast !== true){
24299                 var ds = this.grid.store;
24300             var s = this.selections;
24301             s.each(function(r){
24302                 this.deselectRow(ds.indexOfId(r.id));
24303             }, this);
24304             s.clear();
24305         }else{
24306             this.selections.clear();
24307         }
24308         this.last = false;
24309     },
24310
24311
24312     /**
24313      * Selects all rows.
24314      */
24315     selectAll : function(){
24316         if(this.locked) {
24317             return;
24318         }
24319         this.selections.clear();
24320         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24321             this.selectRow(i, true);
24322         }
24323     },
24324
24325     /**
24326      * Returns True if there is a selection.
24327      * @return {Boolean}
24328      */
24329     hasSelection : function(){
24330         return this.selections.length > 0;
24331     },
24332
24333     /**
24334      * Returns True if the specified row is selected.
24335      * @param {Number/Record} record The record or index of the record to check
24336      * @return {Boolean}
24337      */
24338     isSelected : function(index){
24339             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24340         return (r && this.selections.key(r.id) ? true : false);
24341     },
24342
24343     /**
24344      * Returns True if the specified record id is selected.
24345      * @param {String} id The id of record to check
24346      * @return {Boolean}
24347      */
24348     isIdSelected : function(id){
24349         return (this.selections.key(id) ? true : false);
24350     },
24351
24352
24353     // private
24354     handleMouseDBClick : function(e, t){
24355         
24356     },
24357     // private
24358     handleMouseDown : function(e, t)
24359     {
24360             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24361         if(this.isLocked() || rowIndex < 0 ){
24362             return;
24363         };
24364         if(e.shiftKey && this.last !== false){
24365             var last = this.last;
24366             this.selectRange(last, rowIndex, e.ctrlKey);
24367             this.last = last; // reset the last
24368             t.focus();
24369     
24370         }else{
24371             var isSelected = this.isSelected(rowIndex);
24372             //Roo.log("select row:" + rowIndex);
24373             if(isSelected){
24374                 this.deselectRow(rowIndex);
24375             } else {
24376                         this.selectRow(rowIndex, true);
24377             }
24378     
24379             /*
24380                 if(e.button !== 0 && isSelected){
24381                 alert('rowIndex 2: ' + rowIndex);
24382                     view.focusRow(rowIndex);
24383                 }else if(e.ctrlKey && isSelected){
24384                     this.deselectRow(rowIndex);
24385                 }else if(!isSelected){
24386                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24387                     view.focusRow(rowIndex);
24388                 }
24389             */
24390         }
24391         this.fireEvent("afterselectionchange", this);
24392     },
24393     // private
24394     handleDragableRowClick :  function(grid, rowIndex, e) 
24395     {
24396         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24397             this.selectRow(rowIndex, false);
24398             grid.view.focusRow(rowIndex);
24399              this.fireEvent("afterselectionchange", this);
24400         }
24401     },
24402     
24403     /**
24404      * Selects multiple rows.
24405      * @param {Array} rows Array of the indexes of the row to select
24406      * @param {Boolean} keepExisting (optional) True to keep existing selections
24407      */
24408     selectRows : function(rows, keepExisting){
24409         if(!keepExisting){
24410             this.clearSelections();
24411         }
24412         for(var i = 0, len = rows.length; i < len; i++){
24413             this.selectRow(rows[i], true);
24414         }
24415     },
24416
24417     /**
24418      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24419      * @param {Number} startRow The index of the first row in the range
24420      * @param {Number} endRow The index of the last row in the range
24421      * @param {Boolean} keepExisting (optional) True to retain existing selections
24422      */
24423     selectRange : function(startRow, endRow, keepExisting){
24424         if(this.locked) {
24425             return;
24426         }
24427         if(!keepExisting){
24428             this.clearSelections();
24429         }
24430         if(startRow <= endRow){
24431             for(var i = startRow; i <= endRow; i++){
24432                 this.selectRow(i, true);
24433             }
24434         }else{
24435             for(var i = startRow; i >= endRow; i--){
24436                 this.selectRow(i, true);
24437             }
24438         }
24439     },
24440
24441     /**
24442      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24443      * @param {Number} startRow The index of the first row in the range
24444      * @param {Number} endRow The index of the last row in the range
24445      */
24446     deselectRange : function(startRow, endRow, preventViewNotify){
24447         if(this.locked) {
24448             return;
24449         }
24450         for(var i = startRow; i <= endRow; i++){
24451             this.deselectRow(i, preventViewNotify);
24452         }
24453     },
24454
24455     /**
24456      * Selects a row.
24457      * @param {Number} row The index of the row to select
24458      * @param {Boolean} keepExisting (optional) True to keep existing selections
24459      */
24460     selectRow : function(index, keepExisting, preventViewNotify)
24461     {
24462             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24463             return;
24464         }
24465         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24466             if(!keepExisting || this.singleSelect){
24467                 this.clearSelections();
24468             }
24469             
24470             var r = this.grid.store.getAt(index);
24471             //console.log('selectRow - record id :' + r.id);
24472             
24473             this.selections.add(r);
24474             this.last = this.lastActive = index;
24475             if(!preventViewNotify){
24476                 var proxy = new Roo.Element(
24477                                 this.grid.getRowDom(index)
24478                 );
24479                 proxy.addClass('bg-info info');
24480             }
24481             this.fireEvent("rowselect", this, index, r);
24482             this.fireEvent("selectionchange", this);
24483         }
24484     },
24485
24486     /**
24487      * Deselects a row.
24488      * @param {Number} row The index of the row to deselect
24489      */
24490     deselectRow : function(index, preventViewNotify)
24491     {
24492         if(this.locked) {
24493             return;
24494         }
24495         if(this.last == index){
24496             this.last = false;
24497         }
24498         if(this.lastActive == index){
24499             this.lastActive = false;
24500         }
24501         
24502         var r = this.grid.store.getAt(index);
24503         if (!r) {
24504             return;
24505         }
24506         
24507         this.selections.remove(r);
24508         //.console.log('deselectRow - record id :' + r.id);
24509         if(!preventViewNotify){
24510         
24511             var proxy = new Roo.Element(
24512                 this.grid.getRowDom(index)
24513             );
24514             proxy.removeClass('bg-info info');
24515         }
24516         this.fireEvent("rowdeselect", this, index);
24517         this.fireEvent("selectionchange", this);
24518     },
24519
24520     // private
24521     restoreLast : function(){
24522         if(this._last){
24523             this.last = this._last;
24524         }
24525     },
24526
24527     // private
24528     acceptsNav : function(row, col, cm){
24529         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24530     },
24531
24532     // private
24533     onEditorKey : function(field, e){
24534         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24535         if(k == e.TAB){
24536             e.stopEvent();
24537             ed.completeEdit();
24538             if(e.shiftKey){
24539                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24540             }else{
24541                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24542             }
24543         }else if(k == e.ENTER && !e.ctrlKey){
24544             e.stopEvent();
24545             ed.completeEdit();
24546             if(e.shiftKey){
24547                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24548             }else{
24549                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24550             }
24551         }else if(k == e.ESC){
24552             ed.cancelEdit();
24553         }
24554         if(newCell){
24555             g.startEditing(newCell[0], newCell[1]);
24556         }
24557     }
24558 });
24559 /*
24560  * Based on:
24561  * Ext JS Library 1.1.1
24562  * Copyright(c) 2006-2007, Ext JS, LLC.
24563  *
24564  * Originally Released Under LGPL - original licence link has changed is not relivant.
24565  *
24566  * Fork - LGPL
24567  * <script type="text/javascript">
24568  */
24569  
24570 /**
24571  * @class Roo.bootstrap.PagingToolbar
24572  * @extends Roo.bootstrap.NavSimplebar
24573  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24574  * @constructor
24575  * Create a new PagingToolbar
24576  * @param {Object} config The config object
24577  * @param {Roo.data.Store} store
24578  */
24579 Roo.bootstrap.PagingToolbar = function(config)
24580 {
24581     // old args format still supported... - xtype is prefered..
24582         // created from xtype...
24583     
24584     this.ds = config.dataSource;
24585     
24586     if (config.store && !this.ds) {
24587         this.store= Roo.factory(config.store, Roo.data);
24588         this.ds = this.store;
24589         this.ds.xmodule = this.xmodule || false;
24590     }
24591     
24592     this.toolbarItems = [];
24593     if (config.items) {
24594         this.toolbarItems = config.items;
24595     }
24596     
24597     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24598     
24599     this.cursor = 0;
24600     
24601     if (this.ds) { 
24602         this.bind(this.ds);
24603     }
24604     
24605     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24606     
24607 };
24608
24609 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24610     /**
24611      * @cfg {Roo.data.Store} dataSource
24612      * The underlying data store providing the paged data
24613      */
24614     /**
24615      * @cfg {String/HTMLElement/Element} container
24616      * container The id or element that will contain the toolbar
24617      */
24618     /**
24619      * @cfg {Boolean} displayInfo
24620      * True to display the displayMsg (defaults to false)
24621      */
24622     /**
24623      * @cfg {Number} pageSize
24624      * The number of records to display per page (defaults to 20)
24625      */
24626     pageSize: 20,
24627     /**
24628      * @cfg {String} displayMsg
24629      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24630      */
24631     displayMsg : 'Displaying {0} - {1} of {2}',
24632     /**
24633      * @cfg {String} emptyMsg
24634      * The message to display when no records are found (defaults to "No data to display")
24635      */
24636     emptyMsg : 'No data to display',
24637     /**
24638      * Customizable piece of the default paging text (defaults to "Page")
24639      * @type String
24640      */
24641     beforePageText : "Page",
24642     /**
24643      * Customizable piece of the default paging text (defaults to "of %0")
24644      * @type String
24645      */
24646     afterPageText : "of {0}",
24647     /**
24648      * Customizable piece of the default paging text (defaults to "First Page")
24649      * @type String
24650      */
24651     firstText : "First Page",
24652     /**
24653      * Customizable piece of the default paging text (defaults to "Previous Page")
24654      * @type String
24655      */
24656     prevText : "Previous Page",
24657     /**
24658      * Customizable piece of the default paging text (defaults to "Next Page")
24659      * @type String
24660      */
24661     nextText : "Next Page",
24662     /**
24663      * Customizable piece of the default paging text (defaults to "Last Page")
24664      * @type String
24665      */
24666     lastText : "Last Page",
24667     /**
24668      * Customizable piece of the default paging text (defaults to "Refresh")
24669      * @type String
24670      */
24671     refreshText : "Refresh",
24672
24673     buttons : false,
24674     // private
24675     onRender : function(ct, position) 
24676     {
24677         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24678         this.navgroup.parentId = this.id;
24679         this.navgroup.onRender(this.el, null);
24680         // add the buttons to the navgroup
24681         
24682         if(this.displayInfo){
24683             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24684             this.displayEl = this.el.select('.x-paging-info', true).first();
24685 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24686 //            this.displayEl = navel.el.select('span',true).first();
24687         }
24688         
24689         var _this = this;
24690         
24691         if(this.buttons){
24692             Roo.each(_this.buttons, function(e){ // this might need to use render????
24693                Roo.factory(e).render(_this.el);
24694             });
24695         }
24696             
24697         Roo.each(_this.toolbarItems, function(e) {
24698             _this.navgroup.addItem(e);
24699         });
24700         
24701         
24702         this.first = this.navgroup.addItem({
24703             tooltip: this.firstText,
24704             cls: "prev",
24705             icon : 'fa fa-backward',
24706             disabled: true,
24707             preventDefault: true,
24708             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24709         });
24710         
24711         this.prev =  this.navgroup.addItem({
24712             tooltip: this.prevText,
24713             cls: "prev",
24714             icon : 'fa fa-step-backward',
24715             disabled: true,
24716             preventDefault: true,
24717             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24718         });
24719     //this.addSeparator();
24720         
24721         
24722         var field = this.navgroup.addItem( {
24723             tagtype : 'span',
24724             cls : 'x-paging-position',
24725             
24726             html : this.beforePageText  +
24727                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24728                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24729          } ); //?? escaped?
24730         
24731         this.field = field.el.select('input', true).first();
24732         this.field.on("keydown", this.onPagingKeydown, this);
24733         this.field.on("focus", function(){this.dom.select();});
24734     
24735     
24736         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24737         //this.field.setHeight(18);
24738         //this.addSeparator();
24739         this.next = this.navgroup.addItem({
24740             tooltip: this.nextText,
24741             cls: "next",
24742             html : ' <i class="fa fa-step-forward">',
24743             disabled: true,
24744             preventDefault: true,
24745             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24746         });
24747         this.last = this.navgroup.addItem({
24748             tooltip: this.lastText,
24749             icon : 'fa fa-forward',
24750             cls: "next",
24751             disabled: true,
24752             preventDefault: true,
24753             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24754         });
24755     //this.addSeparator();
24756         this.loading = this.navgroup.addItem({
24757             tooltip: this.refreshText,
24758             icon: 'fa fa-refresh',
24759             preventDefault: true,
24760             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24761         });
24762         
24763     },
24764
24765     // private
24766     updateInfo : function(){
24767         if(this.displayEl){
24768             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24769             var msg = count == 0 ?
24770                 this.emptyMsg :
24771                 String.format(
24772                     this.displayMsg,
24773                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24774                 );
24775             this.displayEl.update(msg);
24776         }
24777     },
24778
24779     // private
24780     onLoad : function(ds, r, o)
24781     {
24782         this.cursor = o.params.start ? o.params.start : 0;
24783         
24784         var d = this.getPageData(),
24785             ap = d.activePage,
24786             ps = d.pages;
24787         
24788         
24789         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24790         this.field.dom.value = ap;
24791         this.first.setDisabled(ap == 1);
24792         this.prev.setDisabled(ap == 1);
24793         this.next.setDisabled(ap == ps);
24794         this.last.setDisabled(ap == ps);
24795         this.loading.enable();
24796         this.updateInfo();
24797     },
24798
24799     // private
24800     getPageData : function(){
24801         var total = this.ds.getTotalCount();
24802         return {
24803             total : total,
24804             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24805             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24806         };
24807     },
24808
24809     // private
24810     onLoadError : function(){
24811         this.loading.enable();
24812     },
24813
24814     // private
24815     onPagingKeydown : function(e){
24816         var k = e.getKey();
24817         var d = this.getPageData();
24818         if(k == e.RETURN){
24819             var v = this.field.dom.value, pageNum;
24820             if(!v || isNaN(pageNum = parseInt(v, 10))){
24821                 this.field.dom.value = d.activePage;
24822                 return;
24823             }
24824             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24825             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24826             e.stopEvent();
24827         }
24828         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))
24829         {
24830           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24831           this.field.dom.value = pageNum;
24832           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24833           e.stopEvent();
24834         }
24835         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24836         {
24837           var v = this.field.dom.value, pageNum; 
24838           var increment = (e.shiftKey) ? 10 : 1;
24839           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24840                 increment *= -1;
24841           }
24842           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24843             this.field.dom.value = d.activePage;
24844             return;
24845           }
24846           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24847           {
24848             this.field.dom.value = parseInt(v, 10) + increment;
24849             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24850             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24851           }
24852           e.stopEvent();
24853         }
24854     },
24855
24856     // private
24857     beforeLoad : function(){
24858         if(this.loading){
24859             this.loading.disable();
24860         }
24861     },
24862
24863     // private
24864     onClick : function(which){
24865         
24866         var ds = this.ds;
24867         if (!ds) {
24868             return;
24869         }
24870         
24871         switch(which){
24872             case "first":
24873                 ds.load({params:{start: 0, limit: this.pageSize}});
24874             break;
24875             case "prev":
24876                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24877             break;
24878             case "next":
24879                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24880             break;
24881             case "last":
24882                 var total = ds.getTotalCount();
24883                 var extra = total % this.pageSize;
24884                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24885                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24886             break;
24887             case "refresh":
24888                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24889             break;
24890         }
24891     },
24892
24893     /**
24894      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24895      * @param {Roo.data.Store} store The data store to unbind
24896      */
24897     unbind : function(ds){
24898         ds.un("beforeload", this.beforeLoad, this);
24899         ds.un("load", this.onLoad, this);
24900         ds.un("loadexception", this.onLoadError, this);
24901         ds.un("remove", this.updateInfo, this);
24902         ds.un("add", this.updateInfo, this);
24903         this.ds = undefined;
24904     },
24905
24906     /**
24907      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24908      * @param {Roo.data.Store} store The data store to bind
24909      */
24910     bind : function(ds){
24911         ds.on("beforeload", this.beforeLoad, this);
24912         ds.on("load", this.onLoad, this);
24913         ds.on("loadexception", this.onLoadError, this);
24914         ds.on("remove", this.updateInfo, this);
24915         ds.on("add", this.updateInfo, this);
24916         this.ds = ds;
24917     }
24918 });/*
24919  * - LGPL
24920  *
24921  * element
24922  * 
24923  */
24924
24925 /**
24926  * @class Roo.bootstrap.MessageBar
24927  * @extends Roo.bootstrap.Component
24928  * Bootstrap MessageBar class
24929  * @cfg {String} html contents of the MessageBar
24930  * @cfg {String} weight (info | success | warning | danger) default info
24931  * @cfg {String} beforeClass insert the bar before the given class
24932  * @cfg {Boolean} closable (true | false) default false
24933  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24934  * 
24935  * @constructor
24936  * Create a new Element
24937  * @param {Object} config The config object
24938  */
24939
24940 Roo.bootstrap.MessageBar = function(config){
24941     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24942 };
24943
24944 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24945     
24946     html: '',
24947     weight: 'info',
24948     closable: false,
24949     fixed: false,
24950     beforeClass: 'bootstrap-sticky-wrap',
24951     
24952     getAutoCreate : function(){
24953         
24954         var cfg = {
24955             tag: 'div',
24956             cls: 'alert alert-dismissable alert-' + this.weight,
24957             cn: [
24958                 {
24959                     tag: 'span',
24960                     cls: 'message',
24961                     html: this.html || ''
24962                 }
24963             ]
24964         };
24965         
24966         if(this.fixed){
24967             cfg.cls += ' alert-messages-fixed';
24968         }
24969         
24970         if(this.closable){
24971             cfg.cn.push({
24972                 tag: 'button',
24973                 cls: 'close',
24974                 html: 'x'
24975             });
24976         }
24977         
24978         return cfg;
24979     },
24980     
24981     onRender : function(ct, position)
24982     {
24983         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24984         
24985         if(!this.el){
24986             var cfg = Roo.apply({},  this.getAutoCreate());
24987             cfg.id = Roo.id();
24988             
24989             if (this.cls) {
24990                 cfg.cls += ' ' + this.cls;
24991             }
24992             if (this.style) {
24993                 cfg.style = this.style;
24994             }
24995             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24996             
24997             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24998         }
24999         
25000         this.el.select('>button.close').on('click', this.hide, this);
25001         
25002     },
25003     
25004     show : function()
25005     {
25006         if (!this.rendered) {
25007             this.render();
25008         }
25009         
25010         this.el.show();
25011         
25012         this.fireEvent('show', this);
25013         
25014     },
25015     
25016     hide : function()
25017     {
25018         if (!this.rendered) {
25019             this.render();
25020         }
25021         
25022         this.el.hide();
25023         
25024         this.fireEvent('hide', this);
25025     },
25026     
25027     update : function()
25028     {
25029 //        var e = this.el.dom.firstChild;
25030 //        
25031 //        if(this.closable){
25032 //            e = e.nextSibling;
25033 //        }
25034 //        
25035 //        e.data = this.html || '';
25036
25037         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25038     }
25039    
25040 });
25041
25042  
25043
25044      /*
25045  * - LGPL
25046  *
25047  * Graph
25048  * 
25049  */
25050
25051
25052 /**
25053  * @class Roo.bootstrap.Graph
25054  * @extends Roo.bootstrap.Component
25055  * Bootstrap Graph class
25056 > Prameters
25057  -sm {number} sm 4
25058  -md {number} md 5
25059  @cfg {String} graphtype  bar | vbar | pie
25060  @cfg {number} g_x coodinator | centre x (pie)
25061  @cfg {number} g_y coodinator | centre y (pie)
25062  @cfg {number} g_r radius (pie)
25063  @cfg {number} g_height height of the chart (respected by all elements in the set)
25064  @cfg {number} g_width width of the chart (respected by all elements in the set)
25065  @cfg {Object} title The title of the chart
25066     
25067  -{Array}  values
25068  -opts (object) options for the chart 
25069      o {
25070      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25071      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25072      o vgutter (number)
25073      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.
25074      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25075      o to
25076      o stretch (boolean)
25077      o }
25078  -opts (object) options for the pie
25079      o{
25080      o cut
25081      o startAngle (number)
25082      o endAngle (number)
25083      } 
25084  *
25085  * @constructor
25086  * Create a new Input
25087  * @param {Object} config The config object
25088  */
25089
25090 Roo.bootstrap.Graph = function(config){
25091     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25092     
25093     this.addEvents({
25094         // img events
25095         /**
25096          * @event click
25097          * The img click event for the img.
25098          * @param {Roo.EventObject} e
25099          */
25100         "click" : true
25101     });
25102 };
25103
25104 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25105     
25106     sm: 4,
25107     md: 5,
25108     graphtype: 'bar',
25109     g_height: 250,
25110     g_width: 400,
25111     g_x: 50,
25112     g_y: 50,
25113     g_r: 30,
25114     opts:{
25115         //g_colors: this.colors,
25116         g_type: 'soft',
25117         g_gutter: '20%'
25118
25119     },
25120     title : false,
25121
25122     getAutoCreate : function(){
25123         
25124         var cfg = {
25125             tag: 'div',
25126             html : null
25127         };
25128         
25129         
25130         return  cfg;
25131     },
25132
25133     onRender : function(ct,position){
25134         
25135         
25136         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25137         
25138         if (typeof(Raphael) == 'undefined') {
25139             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25140             return;
25141         }
25142         
25143         this.raphael = Raphael(this.el.dom);
25144         
25145                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25146                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25147                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25148                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25149                 /*
25150                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25151                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25152                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25153                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25154                 
25155                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25156                 r.barchart(330, 10, 300, 220, data1);
25157                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25158                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25159                 */
25160                 
25161                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25162                 // r.barchart(30, 30, 560, 250,  xdata, {
25163                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25164                 //     axis : "0 0 1 1",
25165                 //     axisxlabels :  xdata
25166                 //     //yvalues : cols,
25167                    
25168                 // });
25169 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25170 //        
25171 //        this.load(null,xdata,{
25172 //                axis : "0 0 1 1",
25173 //                axisxlabels :  xdata
25174 //                });
25175
25176     },
25177
25178     load : function(graphtype,xdata,opts)
25179     {
25180         this.raphael.clear();
25181         if(!graphtype) {
25182             graphtype = this.graphtype;
25183         }
25184         if(!opts){
25185             opts = this.opts;
25186         }
25187         var r = this.raphael,
25188             fin = function () {
25189                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25190             },
25191             fout = function () {
25192                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25193             },
25194             pfin = function() {
25195                 this.sector.stop();
25196                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25197
25198                 if (this.label) {
25199                     this.label[0].stop();
25200                     this.label[0].attr({ r: 7.5 });
25201                     this.label[1].attr({ "font-weight": 800 });
25202                 }
25203             },
25204             pfout = function() {
25205                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25206
25207                 if (this.label) {
25208                     this.label[0].animate({ r: 5 }, 500, "bounce");
25209                     this.label[1].attr({ "font-weight": 400 });
25210                 }
25211             };
25212
25213         switch(graphtype){
25214             case 'bar':
25215                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25216                 break;
25217             case 'hbar':
25218                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25219                 break;
25220             case 'pie':
25221 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25222 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25223 //            
25224                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25225                 
25226                 break;
25227
25228         }
25229         
25230         if(this.title){
25231             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25232         }
25233         
25234     },
25235     
25236     setTitle: function(o)
25237     {
25238         this.title = o;
25239     },
25240     
25241     initEvents: function() {
25242         
25243         if(!this.href){
25244             this.el.on('click', this.onClick, this);
25245         }
25246     },
25247     
25248     onClick : function(e)
25249     {
25250         Roo.log('img onclick');
25251         this.fireEvent('click', this, e);
25252     }
25253    
25254 });
25255
25256  
25257 /*
25258  * - LGPL
25259  *
25260  * numberBox
25261  * 
25262  */
25263 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25264
25265 /**
25266  * @class Roo.bootstrap.dash.NumberBox
25267  * @extends Roo.bootstrap.Component
25268  * Bootstrap NumberBox class
25269  * @cfg {String} headline Box headline
25270  * @cfg {String} content Box content
25271  * @cfg {String} icon Box icon
25272  * @cfg {String} footer Footer text
25273  * @cfg {String} fhref Footer href
25274  * 
25275  * @constructor
25276  * Create a new NumberBox
25277  * @param {Object} config The config object
25278  */
25279
25280
25281 Roo.bootstrap.dash.NumberBox = function(config){
25282     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25283     
25284 };
25285
25286 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25287     
25288     headline : '',
25289     content : '',
25290     icon : '',
25291     footer : '',
25292     fhref : '',
25293     ficon : '',
25294     
25295     getAutoCreate : function(){
25296         
25297         var cfg = {
25298             tag : 'div',
25299             cls : 'small-box ',
25300             cn : [
25301                 {
25302                     tag : 'div',
25303                     cls : 'inner',
25304                     cn :[
25305                         {
25306                             tag : 'h3',
25307                             cls : 'roo-headline',
25308                             html : this.headline
25309                         },
25310                         {
25311                             tag : 'p',
25312                             cls : 'roo-content',
25313                             html : this.content
25314                         }
25315                     ]
25316                 }
25317             ]
25318         };
25319         
25320         if(this.icon){
25321             cfg.cn.push({
25322                 tag : 'div',
25323                 cls : 'icon',
25324                 cn :[
25325                     {
25326                         tag : 'i',
25327                         cls : 'ion ' + this.icon
25328                     }
25329                 ]
25330             });
25331         }
25332         
25333         if(this.footer){
25334             var footer = {
25335                 tag : 'a',
25336                 cls : 'small-box-footer',
25337                 href : this.fhref || '#',
25338                 html : this.footer
25339             };
25340             
25341             cfg.cn.push(footer);
25342             
25343         }
25344         
25345         return  cfg;
25346     },
25347
25348     onRender : function(ct,position){
25349         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25350
25351
25352        
25353                 
25354     },
25355
25356     setHeadline: function (value)
25357     {
25358         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25359     },
25360     
25361     setFooter: function (value, href)
25362     {
25363         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25364         
25365         if(href){
25366             this.el.select('a.small-box-footer',true).first().attr('href', href);
25367         }
25368         
25369     },
25370
25371     setContent: function (value)
25372     {
25373         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25374     },
25375
25376     initEvents: function() 
25377     {   
25378         
25379     }
25380     
25381 });
25382
25383  
25384 /*
25385  * - LGPL
25386  *
25387  * TabBox
25388  * 
25389  */
25390 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25391
25392 /**
25393  * @class Roo.bootstrap.dash.TabBox
25394  * @extends Roo.bootstrap.Component
25395  * Bootstrap TabBox class
25396  * @cfg {String} title Title of the TabBox
25397  * @cfg {String} icon Icon of the TabBox
25398  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25399  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25400  * 
25401  * @constructor
25402  * Create a new TabBox
25403  * @param {Object} config The config object
25404  */
25405
25406
25407 Roo.bootstrap.dash.TabBox = function(config){
25408     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25409     this.addEvents({
25410         // raw events
25411         /**
25412          * @event addpane
25413          * When a pane is added
25414          * @param {Roo.bootstrap.dash.TabPane} pane
25415          */
25416         "addpane" : true,
25417         /**
25418          * @event activatepane
25419          * When a pane is activated
25420          * @param {Roo.bootstrap.dash.TabPane} pane
25421          */
25422         "activatepane" : true
25423         
25424          
25425     });
25426     
25427     this.panes = [];
25428 };
25429
25430 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25431
25432     title : '',
25433     icon : false,
25434     showtabs : true,
25435     tabScrollable : false,
25436     
25437     getChildContainer : function()
25438     {
25439         return this.el.select('.tab-content', true).first();
25440     },
25441     
25442     getAutoCreate : function(){
25443         
25444         var header = {
25445             tag: 'li',
25446             cls: 'pull-left header',
25447             html: this.title,
25448             cn : []
25449         };
25450         
25451         if(this.icon){
25452             header.cn.push({
25453                 tag: 'i',
25454                 cls: 'fa ' + this.icon
25455             });
25456         }
25457         
25458         var h = {
25459             tag: 'ul',
25460             cls: 'nav nav-tabs pull-right',
25461             cn: [
25462                 header
25463             ]
25464         };
25465         
25466         if(this.tabScrollable){
25467             h = {
25468                 tag: 'div',
25469                 cls: 'tab-header',
25470                 cn: [
25471                     {
25472                         tag: 'ul',
25473                         cls: 'nav nav-tabs pull-right',
25474                         cn: [
25475                             header
25476                         ]
25477                     }
25478                 ]
25479             };
25480         }
25481         
25482         var cfg = {
25483             tag: 'div',
25484             cls: 'nav-tabs-custom',
25485             cn: [
25486                 h,
25487                 {
25488                     tag: 'div',
25489                     cls: 'tab-content no-padding',
25490                     cn: []
25491                 }
25492             ]
25493         };
25494
25495         return  cfg;
25496     },
25497     initEvents : function()
25498     {
25499         //Roo.log('add add pane handler');
25500         this.on('addpane', this.onAddPane, this);
25501     },
25502      /**
25503      * Updates the box title
25504      * @param {String} html to set the title to.
25505      */
25506     setTitle : function(value)
25507     {
25508         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25509     },
25510     onAddPane : function(pane)
25511     {
25512         this.panes.push(pane);
25513         //Roo.log('addpane');
25514         //Roo.log(pane);
25515         // tabs are rendere left to right..
25516         if(!this.showtabs){
25517             return;
25518         }
25519         
25520         var ctr = this.el.select('.nav-tabs', true).first();
25521          
25522          
25523         var existing = ctr.select('.nav-tab',true);
25524         var qty = existing.getCount();;
25525         
25526         
25527         var tab = ctr.createChild({
25528             tag : 'li',
25529             cls : 'nav-tab' + (qty ? '' : ' active'),
25530             cn : [
25531                 {
25532                     tag : 'a',
25533                     href:'#',
25534                     html : pane.title
25535                 }
25536             ]
25537         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25538         pane.tab = tab;
25539         
25540         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25541         if (!qty) {
25542             pane.el.addClass('active');
25543         }
25544         
25545                 
25546     },
25547     onTabClick : function(ev,un,ob,pane)
25548     {
25549         //Roo.log('tab - prev default');
25550         ev.preventDefault();
25551         
25552         
25553         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25554         pane.tab.addClass('active');
25555         //Roo.log(pane.title);
25556         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25557         // technically we should have a deactivate event.. but maybe add later.
25558         // and it should not de-activate the selected tab...
25559         this.fireEvent('activatepane', pane);
25560         pane.el.addClass('active');
25561         pane.fireEvent('activate');
25562         
25563         
25564     },
25565     
25566     getActivePane : function()
25567     {
25568         var r = false;
25569         Roo.each(this.panes, function(p) {
25570             if(p.el.hasClass('active')){
25571                 r = p;
25572                 return false;
25573             }
25574             
25575             return;
25576         });
25577         
25578         return r;
25579     }
25580     
25581     
25582 });
25583
25584  
25585 /*
25586  * - LGPL
25587  *
25588  * Tab pane
25589  * 
25590  */
25591 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25592 /**
25593  * @class Roo.bootstrap.TabPane
25594  * @extends Roo.bootstrap.Component
25595  * Bootstrap TabPane class
25596  * @cfg {Boolean} active (false | true) Default false
25597  * @cfg {String} title title of panel
25598
25599  * 
25600  * @constructor
25601  * Create a new TabPane
25602  * @param {Object} config The config object
25603  */
25604
25605 Roo.bootstrap.dash.TabPane = function(config){
25606     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25607     
25608     this.addEvents({
25609         // raw events
25610         /**
25611          * @event activate
25612          * When a pane is activated
25613          * @param {Roo.bootstrap.dash.TabPane} pane
25614          */
25615         "activate" : true
25616          
25617     });
25618 };
25619
25620 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25621     
25622     active : false,
25623     title : '',
25624     
25625     // the tabBox that this is attached to.
25626     tab : false,
25627      
25628     getAutoCreate : function() 
25629     {
25630         var cfg = {
25631             tag: 'div',
25632             cls: 'tab-pane'
25633         };
25634         
25635         if(this.active){
25636             cfg.cls += ' active';
25637         }
25638         
25639         return cfg;
25640     },
25641     initEvents  : function()
25642     {
25643         //Roo.log('trigger add pane handler');
25644         this.parent().fireEvent('addpane', this)
25645     },
25646     
25647      /**
25648      * Updates the tab title 
25649      * @param {String} html to set the title to.
25650      */
25651     setTitle: function(str)
25652     {
25653         if (!this.tab) {
25654             return;
25655         }
25656         this.title = str;
25657         this.tab.select('a', true).first().dom.innerHTML = str;
25658         
25659     }
25660     
25661     
25662     
25663 });
25664
25665  
25666
25667
25668  /*
25669  * - LGPL
25670  *
25671  * menu
25672  * 
25673  */
25674 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25675
25676 /**
25677  * @class Roo.bootstrap.menu.Menu
25678  * @extends Roo.bootstrap.Component
25679  * Bootstrap Menu class - container for Menu
25680  * @cfg {String} html Text of the menu
25681  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25682  * @cfg {String} icon Font awesome icon
25683  * @cfg {String} pos Menu align to (top | bottom) default bottom
25684  * 
25685  * 
25686  * @constructor
25687  * Create a new Menu
25688  * @param {Object} config The config object
25689  */
25690
25691
25692 Roo.bootstrap.menu.Menu = function(config){
25693     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25694     
25695     this.addEvents({
25696         /**
25697          * @event beforeshow
25698          * Fires before this menu is displayed
25699          * @param {Roo.bootstrap.menu.Menu} this
25700          */
25701         beforeshow : true,
25702         /**
25703          * @event beforehide
25704          * Fires before this menu is hidden
25705          * @param {Roo.bootstrap.menu.Menu} this
25706          */
25707         beforehide : true,
25708         /**
25709          * @event show
25710          * Fires after this menu is displayed
25711          * @param {Roo.bootstrap.menu.Menu} this
25712          */
25713         show : true,
25714         /**
25715          * @event hide
25716          * Fires after this menu is hidden
25717          * @param {Roo.bootstrap.menu.Menu} this
25718          */
25719         hide : true,
25720         /**
25721          * @event click
25722          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25723          * @param {Roo.bootstrap.menu.Menu} this
25724          * @param {Roo.EventObject} e
25725          */
25726         click : true
25727     });
25728     
25729 };
25730
25731 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25732     
25733     submenu : false,
25734     html : '',
25735     weight : 'default',
25736     icon : false,
25737     pos : 'bottom',
25738     
25739     
25740     getChildContainer : function() {
25741         if(this.isSubMenu){
25742             return this.el;
25743         }
25744         
25745         return this.el.select('ul.dropdown-menu', true).first();  
25746     },
25747     
25748     getAutoCreate : function()
25749     {
25750         var text = [
25751             {
25752                 tag : 'span',
25753                 cls : 'roo-menu-text',
25754                 html : this.html
25755             }
25756         ];
25757         
25758         if(this.icon){
25759             text.unshift({
25760                 tag : 'i',
25761                 cls : 'fa ' + this.icon
25762             })
25763         }
25764         
25765         
25766         var cfg = {
25767             tag : 'div',
25768             cls : 'btn-group',
25769             cn : [
25770                 {
25771                     tag : 'button',
25772                     cls : 'dropdown-button btn btn-' + this.weight,
25773                     cn : text
25774                 },
25775                 {
25776                     tag : 'button',
25777                     cls : 'dropdown-toggle btn btn-' + this.weight,
25778                     cn : [
25779                         {
25780                             tag : 'span',
25781                             cls : 'caret'
25782                         }
25783                     ]
25784                 },
25785                 {
25786                     tag : 'ul',
25787                     cls : 'dropdown-menu'
25788                 }
25789             ]
25790             
25791         };
25792         
25793         if(this.pos == 'top'){
25794             cfg.cls += ' dropup';
25795         }
25796         
25797         if(this.isSubMenu){
25798             cfg = {
25799                 tag : 'ul',
25800                 cls : 'dropdown-menu'
25801             }
25802         }
25803         
25804         return cfg;
25805     },
25806     
25807     onRender : function(ct, position)
25808     {
25809         this.isSubMenu = ct.hasClass('dropdown-submenu');
25810         
25811         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25812     },
25813     
25814     initEvents : function() 
25815     {
25816         if(this.isSubMenu){
25817             return;
25818         }
25819         
25820         this.hidden = true;
25821         
25822         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25823         this.triggerEl.on('click', this.onTriggerPress, this);
25824         
25825         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25826         this.buttonEl.on('click', this.onClick, this);
25827         
25828     },
25829     
25830     list : function()
25831     {
25832         if(this.isSubMenu){
25833             return this.el;
25834         }
25835         
25836         return this.el.select('ul.dropdown-menu', true).first();
25837     },
25838     
25839     onClick : function(e)
25840     {
25841         this.fireEvent("click", this, e);
25842     },
25843     
25844     onTriggerPress  : function(e)
25845     {   
25846         if (this.isVisible()) {
25847             this.hide();
25848         } else {
25849             this.show();
25850         }
25851     },
25852     
25853     isVisible : function(){
25854         return !this.hidden;
25855     },
25856     
25857     show : function()
25858     {
25859         this.fireEvent("beforeshow", this);
25860         
25861         this.hidden = false;
25862         this.el.addClass('open');
25863         
25864         Roo.get(document).on("mouseup", this.onMouseUp, this);
25865         
25866         this.fireEvent("show", this);
25867         
25868         
25869     },
25870     
25871     hide : function()
25872     {
25873         this.fireEvent("beforehide", this);
25874         
25875         this.hidden = true;
25876         this.el.removeClass('open');
25877         
25878         Roo.get(document).un("mouseup", this.onMouseUp);
25879         
25880         this.fireEvent("hide", this);
25881     },
25882     
25883     onMouseUp : function()
25884     {
25885         this.hide();
25886     }
25887     
25888 });
25889
25890  
25891  /*
25892  * - LGPL
25893  *
25894  * menu item
25895  * 
25896  */
25897 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25898
25899 /**
25900  * @class Roo.bootstrap.menu.Item
25901  * @extends Roo.bootstrap.Component
25902  * Bootstrap MenuItem class
25903  * @cfg {Boolean} submenu (true | false) default false
25904  * @cfg {String} html text of the item
25905  * @cfg {String} href the link
25906  * @cfg {Boolean} disable (true | false) default false
25907  * @cfg {Boolean} preventDefault (true | false) default true
25908  * @cfg {String} icon Font awesome icon
25909  * @cfg {String} pos Submenu align to (left | right) default right 
25910  * 
25911  * 
25912  * @constructor
25913  * Create a new Item
25914  * @param {Object} config The config object
25915  */
25916
25917
25918 Roo.bootstrap.menu.Item = function(config){
25919     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25920     this.addEvents({
25921         /**
25922          * @event mouseover
25923          * Fires when the mouse is hovering over this menu
25924          * @param {Roo.bootstrap.menu.Item} this
25925          * @param {Roo.EventObject} e
25926          */
25927         mouseover : true,
25928         /**
25929          * @event mouseout
25930          * Fires when the mouse exits this menu
25931          * @param {Roo.bootstrap.menu.Item} this
25932          * @param {Roo.EventObject} e
25933          */
25934         mouseout : true,
25935         // raw events
25936         /**
25937          * @event click
25938          * The raw click event for the entire grid.
25939          * @param {Roo.EventObject} e
25940          */
25941         click : true
25942     });
25943 };
25944
25945 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25946     
25947     submenu : false,
25948     href : '',
25949     html : '',
25950     preventDefault: true,
25951     disable : false,
25952     icon : false,
25953     pos : 'right',
25954     
25955     getAutoCreate : function()
25956     {
25957         var text = [
25958             {
25959                 tag : 'span',
25960                 cls : 'roo-menu-item-text',
25961                 html : this.html
25962             }
25963         ];
25964         
25965         if(this.icon){
25966             text.unshift({
25967                 tag : 'i',
25968                 cls : 'fa ' + this.icon
25969             })
25970         }
25971         
25972         var cfg = {
25973             tag : 'li',
25974             cn : [
25975                 {
25976                     tag : 'a',
25977                     href : this.href || '#',
25978                     cn : text
25979                 }
25980             ]
25981         };
25982         
25983         if(this.disable){
25984             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25985         }
25986         
25987         if(this.submenu){
25988             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25989             
25990             if(this.pos == 'left'){
25991                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25992             }
25993         }
25994         
25995         return cfg;
25996     },
25997     
25998     initEvents : function() 
25999     {
26000         this.el.on('mouseover', this.onMouseOver, this);
26001         this.el.on('mouseout', this.onMouseOut, this);
26002         
26003         this.el.select('a', true).first().on('click', this.onClick, this);
26004         
26005     },
26006     
26007     onClick : function(e)
26008     {
26009         if(this.preventDefault){
26010             e.preventDefault();
26011         }
26012         
26013         this.fireEvent("click", this, e);
26014     },
26015     
26016     onMouseOver : function(e)
26017     {
26018         if(this.submenu && this.pos == 'left'){
26019             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26020         }
26021         
26022         this.fireEvent("mouseover", this, e);
26023     },
26024     
26025     onMouseOut : function(e)
26026     {
26027         this.fireEvent("mouseout", this, e);
26028     }
26029 });
26030
26031  
26032
26033  /*
26034  * - LGPL
26035  *
26036  * menu separator
26037  * 
26038  */
26039 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26040
26041 /**
26042  * @class Roo.bootstrap.menu.Separator
26043  * @extends Roo.bootstrap.Component
26044  * Bootstrap Separator class
26045  * 
26046  * @constructor
26047  * Create a new Separator
26048  * @param {Object} config The config object
26049  */
26050
26051
26052 Roo.bootstrap.menu.Separator = function(config){
26053     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26054 };
26055
26056 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26057     
26058     getAutoCreate : function(){
26059         var cfg = {
26060             tag : 'li',
26061             cls: 'divider'
26062         };
26063         
26064         return cfg;
26065     }
26066    
26067 });
26068
26069  
26070
26071  /*
26072  * - LGPL
26073  *
26074  * Tooltip
26075  * 
26076  */
26077
26078 /**
26079  * @class Roo.bootstrap.Tooltip
26080  * Bootstrap Tooltip class
26081  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26082  * to determine which dom element triggers the tooltip.
26083  * 
26084  * It needs to add support for additional attributes like tooltip-position
26085  * 
26086  * @constructor
26087  * Create a new Toolti
26088  * @param {Object} config The config object
26089  */
26090
26091 Roo.bootstrap.Tooltip = function(config){
26092     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26093     
26094     this.alignment = Roo.bootstrap.Tooltip.alignment;
26095     
26096     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26097         this.alignment = config.alignment;
26098     }
26099     
26100 };
26101
26102 Roo.apply(Roo.bootstrap.Tooltip, {
26103     /**
26104      * @function init initialize tooltip monitoring.
26105      * @static
26106      */
26107     currentEl : false,
26108     currentTip : false,
26109     currentRegion : false,
26110     
26111     //  init : delay?
26112     
26113     init : function()
26114     {
26115         Roo.get(document).on('mouseover', this.enter ,this);
26116         Roo.get(document).on('mouseout', this.leave, this);
26117          
26118         
26119         this.currentTip = new Roo.bootstrap.Tooltip();
26120     },
26121     
26122     enter : function(ev)
26123     {
26124         var dom = ev.getTarget();
26125         
26126         //Roo.log(['enter',dom]);
26127         var el = Roo.fly(dom);
26128         if (this.currentEl) {
26129             //Roo.log(dom);
26130             //Roo.log(this.currentEl);
26131             //Roo.log(this.currentEl.contains(dom));
26132             if (this.currentEl == el) {
26133                 return;
26134             }
26135             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26136                 return;
26137             }
26138
26139         }
26140         
26141         if (this.currentTip.el) {
26142             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26143         }    
26144         //Roo.log(ev);
26145         
26146         if(!el || el.dom == document){
26147             return;
26148         }
26149         
26150         var bindEl = el;
26151         
26152         // you can not look for children, as if el is the body.. then everythign is the child..
26153         if (!el.attr('tooltip')) { //
26154             if (!el.select("[tooltip]").elements.length) {
26155                 return;
26156             }
26157             // is the mouse over this child...?
26158             bindEl = el.select("[tooltip]").first();
26159             var xy = ev.getXY();
26160             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26161                 //Roo.log("not in region.");
26162                 return;
26163             }
26164             //Roo.log("child element over..");
26165             
26166         }
26167         this.currentEl = bindEl;
26168         this.currentTip.bind(bindEl);
26169         this.currentRegion = Roo.lib.Region.getRegion(dom);
26170         this.currentTip.enter();
26171         
26172     },
26173     leave : function(ev)
26174     {
26175         var dom = ev.getTarget();
26176         //Roo.log(['leave',dom]);
26177         if (!this.currentEl) {
26178             return;
26179         }
26180         
26181         
26182         if (dom != this.currentEl.dom) {
26183             return;
26184         }
26185         var xy = ev.getXY();
26186         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26187             return;
26188         }
26189         // only activate leave if mouse cursor is outside... bounding box..
26190         
26191         
26192         
26193         
26194         if (this.currentTip) {
26195             this.currentTip.leave();
26196         }
26197         //Roo.log('clear currentEl');
26198         this.currentEl = false;
26199         
26200         
26201     },
26202     alignment : {
26203         'left' : ['r-l', [-2,0], 'right'],
26204         'right' : ['l-r', [2,0], 'left'],
26205         'bottom' : ['t-b', [0,2], 'top'],
26206         'top' : [ 'b-t', [0,-2], 'bottom']
26207     }
26208     
26209 });
26210
26211
26212 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26213     
26214     
26215     bindEl : false,
26216     
26217     delay : null, // can be { show : 300 , hide: 500}
26218     
26219     timeout : null,
26220     
26221     hoverState : null, //???
26222     
26223     placement : 'bottom', 
26224     
26225     alignment : false,
26226     
26227     getAutoCreate : function(){
26228     
26229         var cfg = {
26230            cls : 'tooltip',
26231            role : 'tooltip',
26232            cn : [
26233                 {
26234                     cls : 'tooltip-arrow'
26235                 },
26236                 {
26237                     cls : 'tooltip-inner'
26238                 }
26239            ]
26240         };
26241         
26242         return cfg;
26243     },
26244     bind : function(el)
26245     {
26246         this.bindEl = el;
26247     },
26248       
26249     
26250     enter : function () {
26251        
26252         if (this.timeout != null) {
26253             clearTimeout(this.timeout);
26254         }
26255         
26256         this.hoverState = 'in';
26257          //Roo.log("enter - show");
26258         if (!this.delay || !this.delay.show) {
26259             this.show();
26260             return;
26261         }
26262         var _t = this;
26263         this.timeout = setTimeout(function () {
26264             if (_t.hoverState == 'in') {
26265                 _t.show();
26266             }
26267         }, this.delay.show);
26268     },
26269     leave : function()
26270     {
26271         clearTimeout(this.timeout);
26272     
26273         this.hoverState = 'out';
26274          if (!this.delay || !this.delay.hide) {
26275             this.hide();
26276             return;
26277         }
26278        
26279         var _t = this;
26280         this.timeout = setTimeout(function () {
26281             //Roo.log("leave - timeout");
26282             
26283             if (_t.hoverState == 'out') {
26284                 _t.hide();
26285                 Roo.bootstrap.Tooltip.currentEl = false;
26286             }
26287         }, delay);
26288     },
26289     
26290     show : function (msg)
26291     {
26292         if (!this.el) {
26293             this.render(document.body);
26294         }
26295         // set content.
26296         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26297         
26298         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26299         
26300         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26301         
26302         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26303         
26304         var placement = typeof this.placement == 'function' ?
26305             this.placement.call(this, this.el, on_el) :
26306             this.placement;
26307             
26308         var autoToken = /\s?auto?\s?/i;
26309         var autoPlace = autoToken.test(placement);
26310         if (autoPlace) {
26311             placement = placement.replace(autoToken, '') || 'top';
26312         }
26313         
26314         //this.el.detach()
26315         //this.el.setXY([0,0]);
26316         this.el.show();
26317         //this.el.dom.style.display='block';
26318         
26319         //this.el.appendTo(on_el);
26320         
26321         var p = this.getPosition();
26322         var box = this.el.getBox();
26323         
26324         if (autoPlace) {
26325             // fixme..
26326         }
26327         
26328         var align = this.alignment[placement];
26329         
26330         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26331         
26332         if(placement == 'top' || placement == 'bottom'){
26333             if(xy[0] < 0){
26334                 placement = 'right';
26335             }
26336             
26337             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26338                 placement = 'left';
26339             }
26340             
26341             var scroll = Roo.select('body', true).first().getScroll();
26342             
26343             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26344                 placement = 'top';
26345             }
26346             
26347             align = this.alignment[placement];
26348         }
26349         
26350         this.el.alignTo(this.bindEl, align[0],align[1]);
26351         //var arrow = this.el.select('.arrow',true).first();
26352         //arrow.set(align[2], 
26353         
26354         this.el.addClass(placement);
26355         
26356         this.el.addClass('in fade');
26357         
26358         this.hoverState = null;
26359         
26360         if (this.el.hasClass('fade')) {
26361             // fade it?
26362         }
26363         
26364     },
26365     hide : function()
26366     {
26367          
26368         if (!this.el) {
26369             return;
26370         }
26371         //this.el.setXY([0,0]);
26372         this.el.removeClass('in');
26373         //this.el.hide();
26374         
26375     }
26376     
26377 });
26378  
26379
26380  /*
26381  * - LGPL
26382  *
26383  * Location Picker
26384  * 
26385  */
26386
26387 /**
26388  * @class Roo.bootstrap.LocationPicker
26389  * @extends Roo.bootstrap.Component
26390  * Bootstrap LocationPicker class
26391  * @cfg {Number} latitude Position when init default 0
26392  * @cfg {Number} longitude Position when init default 0
26393  * @cfg {Number} zoom default 15
26394  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26395  * @cfg {Boolean} mapTypeControl default false
26396  * @cfg {Boolean} disableDoubleClickZoom default false
26397  * @cfg {Boolean} scrollwheel default true
26398  * @cfg {Boolean} streetViewControl default false
26399  * @cfg {Number} radius default 0
26400  * @cfg {String} locationName
26401  * @cfg {Boolean} draggable default true
26402  * @cfg {Boolean} enableAutocomplete default false
26403  * @cfg {Boolean} enableReverseGeocode default true
26404  * @cfg {String} markerTitle
26405  * 
26406  * @constructor
26407  * Create a new LocationPicker
26408  * @param {Object} config The config object
26409  */
26410
26411
26412 Roo.bootstrap.LocationPicker = function(config){
26413     
26414     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26415     
26416     this.addEvents({
26417         /**
26418          * @event initial
26419          * Fires when the picker initialized.
26420          * @param {Roo.bootstrap.LocationPicker} this
26421          * @param {Google Location} location
26422          */
26423         initial : true,
26424         /**
26425          * @event positionchanged
26426          * Fires when the picker position changed.
26427          * @param {Roo.bootstrap.LocationPicker} this
26428          * @param {Google Location} location
26429          */
26430         positionchanged : true,
26431         /**
26432          * @event resize
26433          * Fires when the map resize.
26434          * @param {Roo.bootstrap.LocationPicker} this
26435          */
26436         resize : true,
26437         /**
26438          * @event show
26439          * Fires when the map show.
26440          * @param {Roo.bootstrap.LocationPicker} this
26441          */
26442         show : true,
26443         /**
26444          * @event hide
26445          * Fires when the map hide.
26446          * @param {Roo.bootstrap.LocationPicker} this
26447          */
26448         hide : true,
26449         /**
26450          * @event mapClick
26451          * Fires when click the map.
26452          * @param {Roo.bootstrap.LocationPicker} this
26453          * @param {Map event} e
26454          */
26455         mapClick : true,
26456         /**
26457          * @event mapRightClick
26458          * Fires when right click the map.
26459          * @param {Roo.bootstrap.LocationPicker} this
26460          * @param {Map event} e
26461          */
26462         mapRightClick : true,
26463         /**
26464          * @event markerClick
26465          * Fires when click the marker.
26466          * @param {Roo.bootstrap.LocationPicker} this
26467          * @param {Map event} e
26468          */
26469         markerClick : true,
26470         /**
26471          * @event markerRightClick
26472          * Fires when right click the marker.
26473          * @param {Roo.bootstrap.LocationPicker} this
26474          * @param {Map event} e
26475          */
26476         markerRightClick : true,
26477         /**
26478          * @event OverlayViewDraw
26479          * Fires when OverlayView Draw
26480          * @param {Roo.bootstrap.LocationPicker} this
26481          */
26482         OverlayViewDraw : true,
26483         /**
26484          * @event OverlayViewOnAdd
26485          * Fires when OverlayView Draw
26486          * @param {Roo.bootstrap.LocationPicker} this
26487          */
26488         OverlayViewOnAdd : true,
26489         /**
26490          * @event OverlayViewOnRemove
26491          * Fires when OverlayView Draw
26492          * @param {Roo.bootstrap.LocationPicker} this
26493          */
26494         OverlayViewOnRemove : true,
26495         /**
26496          * @event OverlayViewShow
26497          * Fires when OverlayView Draw
26498          * @param {Roo.bootstrap.LocationPicker} this
26499          * @param {Pixel} cpx
26500          */
26501         OverlayViewShow : true,
26502         /**
26503          * @event OverlayViewHide
26504          * Fires when OverlayView Draw
26505          * @param {Roo.bootstrap.LocationPicker} this
26506          */
26507         OverlayViewHide : true,
26508         /**
26509          * @event loadexception
26510          * Fires when load google lib failed.
26511          * @param {Roo.bootstrap.LocationPicker} this
26512          */
26513         loadexception : true
26514     });
26515         
26516 };
26517
26518 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26519     
26520     gMapContext: false,
26521     
26522     latitude: 0,
26523     longitude: 0,
26524     zoom: 15,
26525     mapTypeId: false,
26526     mapTypeControl: false,
26527     disableDoubleClickZoom: false,
26528     scrollwheel: true,
26529     streetViewControl: false,
26530     radius: 0,
26531     locationName: '',
26532     draggable: true,
26533     enableAutocomplete: false,
26534     enableReverseGeocode: true,
26535     markerTitle: '',
26536     
26537     getAutoCreate: function()
26538     {
26539
26540         var cfg = {
26541             tag: 'div',
26542             cls: 'roo-location-picker'
26543         };
26544         
26545         return cfg
26546     },
26547     
26548     initEvents: function(ct, position)
26549     {       
26550         if(!this.el.getWidth() || this.isApplied()){
26551             return;
26552         }
26553         
26554         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26555         
26556         this.initial();
26557     },
26558     
26559     initial: function()
26560     {
26561         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26562             this.fireEvent('loadexception', this);
26563             return;
26564         }
26565         
26566         if(!this.mapTypeId){
26567             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26568         }
26569         
26570         this.gMapContext = this.GMapContext();
26571         
26572         this.initOverlayView();
26573         
26574         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26575         
26576         var _this = this;
26577                 
26578         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26579             _this.setPosition(_this.gMapContext.marker.position);
26580         });
26581         
26582         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26583             _this.fireEvent('mapClick', this, event);
26584             
26585         });
26586
26587         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26588             _this.fireEvent('mapRightClick', this, event);
26589             
26590         });
26591         
26592         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26593             _this.fireEvent('markerClick', this, event);
26594             
26595         });
26596
26597         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26598             _this.fireEvent('markerRightClick', this, event);
26599             
26600         });
26601         
26602         this.setPosition(this.gMapContext.location);
26603         
26604         this.fireEvent('initial', this, this.gMapContext.location);
26605     },
26606     
26607     initOverlayView: function()
26608     {
26609         var _this = this;
26610         
26611         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26612             
26613             draw: function()
26614             {
26615                 _this.fireEvent('OverlayViewDraw', _this);
26616             },
26617             
26618             onAdd: function()
26619             {
26620                 _this.fireEvent('OverlayViewOnAdd', _this);
26621             },
26622             
26623             onRemove: function()
26624             {
26625                 _this.fireEvent('OverlayViewOnRemove', _this);
26626             },
26627             
26628             show: function(cpx)
26629             {
26630                 _this.fireEvent('OverlayViewShow', _this, cpx);
26631             },
26632             
26633             hide: function()
26634             {
26635                 _this.fireEvent('OverlayViewHide', _this);
26636             }
26637             
26638         });
26639     },
26640     
26641     fromLatLngToContainerPixel: function(event)
26642     {
26643         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26644     },
26645     
26646     isApplied: function() 
26647     {
26648         return this.getGmapContext() == false ? false : true;
26649     },
26650     
26651     getGmapContext: function() 
26652     {
26653         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26654     },
26655     
26656     GMapContext: function() 
26657     {
26658         var position = new google.maps.LatLng(this.latitude, this.longitude);
26659         
26660         var _map = new google.maps.Map(this.el.dom, {
26661             center: position,
26662             zoom: this.zoom,
26663             mapTypeId: this.mapTypeId,
26664             mapTypeControl: this.mapTypeControl,
26665             disableDoubleClickZoom: this.disableDoubleClickZoom,
26666             scrollwheel: this.scrollwheel,
26667             streetViewControl: this.streetViewControl,
26668             locationName: this.locationName,
26669             draggable: this.draggable,
26670             enableAutocomplete: this.enableAutocomplete,
26671             enableReverseGeocode: this.enableReverseGeocode
26672         });
26673         
26674         var _marker = new google.maps.Marker({
26675             position: position,
26676             map: _map,
26677             title: this.markerTitle,
26678             draggable: this.draggable
26679         });
26680         
26681         return {
26682             map: _map,
26683             marker: _marker,
26684             circle: null,
26685             location: position,
26686             radius: this.radius,
26687             locationName: this.locationName,
26688             addressComponents: {
26689                 formatted_address: null,
26690                 addressLine1: null,
26691                 addressLine2: null,
26692                 streetName: null,
26693                 streetNumber: null,
26694                 city: null,
26695                 district: null,
26696                 state: null,
26697                 stateOrProvince: null
26698             },
26699             settings: this,
26700             domContainer: this.el.dom,
26701             geodecoder: new google.maps.Geocoder()
26702         };
26703     },
26704     
26705     drawCircle: function(center, radius, options) 
26706     {
26707         if (this.gMapContext.circle != null) {
26708             this.gMapContext.circle.setMap(null);
26709         }
26710         if (radius > 0) {
26711             radius *= 1;
26712             options = Roo.apply({}, options, {
26713                 strokeColor: "#0000FF",
26714                 strokeOpacity: .35,
26715                 strokeWeight: 2,
26716                 fillColor: "#0000FF",
26717                 fillOpacity: .2
26718             });
26719             
26720             options.map = this.gMapContext.map;
26721             options.radius = radius;
26722             options.center = center;
26723             this.gMapContext.circle = new google.maps.Circle(options);
26724             return this.gMapContext.circle;
26725         }
26726         
26727         return null;
26728     },
26729     
26730     setPosition: function(location) 
26731     {
26732         this.gMapContext.location = location;
26733         this.gMapContext.marker.setPosition(location);
26734         this.gMapContext.map.panTo(location);
26735         this.drawCircle(location, this.gMapContext.radius, {});
26736         
26737         var _this = this;
26738         
26739         if (this.gMapContext.settings.enableReverseGeocode) {
26740             this.gMapContext.geodecoder.geocode({
26741                 latLng: this.gMapContext.location
26742             }, function(results, status) {
26743                 
26744                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26745                     _this.gMapContext.locationName = results[0].formatted_address;
26746                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26747                     
26748                     _this.fireEvent('positionchanged', this, location);
26749                 }
26750             });
26751             
26752             return;
26753         }
26754         
26755         this.fireEvent('positionchanged', this, location);
26756     },
26757     
26758     resize: function()
26759     {
26760         google.maps.event.trigger(this.gMapContext.map, "resize");
26761         
26762         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26763         
26764         this.fireEvent('resize', this);
26765     },
26766     
26767     setPositionByLatLng: function(latitude, longitude)
26768     {
26769         this.setPosition(new google.maps.LatLng(latitude, longitude));
26770     },
26771     
26772     getCurrentPosition: function() 
26773     {
26774         return {
26775             latitude: this.gMapContext.location.lat(),
26776             longitude: this.gMapContext.location.lng()
26777         };
26778     },
26779     
26780     getAddressName: function() 
26781     {
26782         return this.gMapContext.locationName;
26783     },
26784     
26785     getAddressComponents: function() 
26786     {
26787         return this.gMapContext.addressComponents;
26788     },
26789     
26790     address_component_from_google_geocode: function(address_components) 
26791     {
26792         var result = {};
26793         
26794         for (var i = 0; i < address_components.length; i++) {
26795             var component = address_components[i];
26796             if (component.types.indexOf("postal_code") >= 0) {
26797                 result.postalCode = component.short_name;
26798             } else if (component.types.indexOf("street_number") >= 0) {
26799                 result.streetNumber = component.short_name;
26800             } else if (component.types.indexOf("route") >= 0) {
26801                 result.streetName = component.short_name;
26802             } else if (component.types.indexOf("neighborhood") >= 0) {
26803                 result.city = component.short_name;
26804             } else if (component.types.indexOf("locality") >= 0) {
26805                 result.city = component.short_name;
26806             } else if (component.types.indexOf("sublocality") >= 0) {
26807                 result.district = component.short_name;
26808             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26809                 result.stateOrProvince = component.short_name;
26810             } else if (component.types.indexOf("country") >= 0) {
26811                 result.country = component.short_name;
26812             }
26813         }
26814         
26815         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26816         result.addressLine2 = "";
26817         return result;
26818     },
26819     
26820     setZoomLevel: function(zoom)
26821     {
26822         this.gMapContext.map.setZoom(zoom);
26823     },
26824     
26825     show: function()
26826     {
26827         if(!this.el){
26828             return;
26829         }
26830         
26831         this.el.show();
26832         
26833         this.resize();
26834         
26835         this.fireEvent('show', this);
26836     },
26837     
26838     hide: function()
26839     {
26840         if(!this.el){
26841             return;
26842         }
26843         
26844         this.el.hide();
26845         
26846         this.fireEvent('hide', this);
26847     }
26848     
26849 });
26850
26851 Roo.apply(Roo.bootstrap.LocationPicker, {
26852     
26853     OverlayView : function(map, options)
26854     {
26855         options = options || {};
26856         
26857         this.setMap(map);
26858     }
26859     
26860     
26861 });/*
26862  * - LGPL
26863  *
26864  * Alert
26865  * 
26866  */
26867
26868 /**
26869  * @class Roo.bootstrap.Alert
26870  * @extends Roo.bootstrap.Component
26871  * Bootstrap Alert class
26872  * @cfg {String} title The title of alert
26873  * @cfg {String} html The content of alert
26874  * @cfg {String} weight (  success | info | warning | danger )
26875  * @cfg {String} faicon font-awesomeicon
26876  * 
26877  * @constructor
26878  * Create a new alert
26879  * @param {Object} config The config object
26880  */
26881
26882
26883 Roo.bootstrap.Alert = function(config){
26884     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26885     
26886 };
26887
26888 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26889     
26890     title: '',
26891     html: '',
26892     weight: false,
26893     faicon: false,
26894     
26895     getAutoCreate : function()
26896     {
26897         
26898         var cfg = {
26899             tag : 'div',
26900             cls : 'alert',
26901             cn : [
26902                 {
26903                     tag : 'i',
26904                     cls : 'roo-alert-icon'
26905                     
26906                 },
26907                 {
26908                     tag : 'b',
26909                     cls : 'roo-alert-title',
26910                     html : this.title
26911                 },
26912                 {
26913                     tag : 'span',
26914                     cls : 'roo-alert-text',
26915                     html : this.html
26916                 }
26917             ]
26918         };
26919         
26920         if(this.faicon){
26921             cfg.cn[0].cls += ' fa ' + this.faicon;
26922         }
26923         
26924         if(this.weight){
26925             cfg.cls += ' alert-' + this.weight;
26926         }
26927         
26928         return cfg;
26929     },
26930     
26931     initEvents: function() 
26932     {
26933         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26934     },
26935     
26936     setTitle : function(str)
26937     {
26938         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26939     },
26940     
26941     setText : function(str)
26942     {
26943         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26944     },
26945     
26946     setWeight : function(weight)
26947     {
26948         if(this.weight){
26949             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26950         }
26951         
26952         this.weight = weight;
26953         
26954         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26955     },
26956     
26957     setIcon : function(icon)
26958     {
26959         if(this.faicon){
26960             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26961         }
26962         
26963         this.faicon = icon;
26964         
26965         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26966     },
26967     
26968     hide: function() 
26969     {
26970         this.el.hide();   
26971     },
26972     
26973     show: function() 
26974     {  
26975         this.el.show();   
26976     }
26977     
26978 });
26979
26980  
26981 /*
26982 * Licence: LGPL
26983 */
26984
26985 /**
26986  * @class Roo.bootstrap.UploadCropbox
26987  * @extends Roo.bootstrap.Component
26988  * Bootstrap UploadCropbox class
26989  * @cfg {String} emptyText show when image has been loaded
26990  * @cfg {String} rotateNotify show when image too small to rotate
26991  * @cfg {Number} errorTimeout default 3000
26992  * @cfg {Number} minWidth default 300
26993  * @cfg {Number} minHeight default 300
26994  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26995  * @cfg {Boolean} isDocument (true|false) default false
26996  * @cfg {String} url action url
26997  * @cfg {String} paramName default 'imageUpload'
26998  * @cfg {String} method default POST
26999  * @cfg {Boolean} loadMask (true|false) default true
27000  * @cfg {Boolean} loadingText default 'Loading...'
27001  * 
27002  * @constructor
27003  * Create a new UploadCropbox
27004  * @param {Object} config The config object
27005  */
27006
27007 Roo.bootstrap.UploadCropbox = function(config){
27008     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27009     
27010     this.addEvents({
27011         /**
27012          * @event beforeselectfile
27013          * Fire before select file
27014          * @param {Roo.bootstrap.UploadCropbox} this
27015          */
27016         "beforeselectfile" : true,
27017         /**
27018          * @event initial
27019          * Fire after initEvent
27020          * @param {Roo.bootstrap.UploadCropbox} this
27021          */
27022         "initial" : true,
27023         /**
27024          * @event crop
27025          * Fire after initEvent
27026          * @param {Roo.bootstrap.UploadCropbox} this
27027          * @param {String} data
27028          */
27029         "crop" : true,
27030         /**
27031          * @event prepare
27032          * Fire when preparing the file data
27033          * @param {Roo.bootstrap.UploadCropbox} this
27034          * @param {Object} file
27035          */
27036         "prepare" : true,
27037         /**
27038          * @event exception
27039          * Fire when get exception
27040          * @param {Roo.bootstrap.UploadCropbox} this
27041          * @param {XMLHttpRequest} xhr
27042          */
27043         "exception" : true,
27044         /**
27045          * @event beforeloadcanvas
27046          * Fire before load the canvas
27047          * @param {Roo.bootstrap.UploadCropbox} this
27048          * @param {String} src
27049          */
27050         "beforeloadcanvas" : true,
27051         /**
27052          * @event trash
27053          * Fire when trash image
27054          * @param {Roo.bootstrap.UploadCropbox} this
27055          */
27056         "trash" : true,
27057         /**
27058          * @event download
27059          * Fire when download the image
27060          * @param {Roo.bootstrap.UploadCropbox} this
27061          */
27062         "download" : true,
27063         /**
27064          * @event footerbuttonclick
27065          * Fire when footerbuttonclick
27066          * @param {Roo.bootstrap.UploadCropbox} this
27067          * @param {String} type
27068          */
27069         "footerbuttonclick" : true,
27070         /**
27071          * @event resize
27072          * Fire when resize
27073          * @param {Roo.bootstrap.UploadCropbox} this
27074          */
27075         "resize" : true,
27076         /**
27077          * @event rotate
27078          * Fire when rotate the image
27079          * @param {Roo.bootstrap.UploadCropbox} this
27080          * @param {String} pos
27081          */
27082         "rotate" : true,
27083         /**
27084          * @event inspect
27085          * Fire when inspect the file
27086          * @param {Roo.bootstrap.UploadCropbox} this
27087          * @param {Object} file
27088          */
27089         "inspect" : true,
27090         /**
27091          * @event upload
27092          * Fire when xhr upload the file
27093          * @param {Roo.bootstrap.UploadCropbox} this
27094          * @param {Object} data
27095          */
27096         "upload" : true,
27097         /**
27098          * @event arrange
27099          * Fire when arrange the file data
27100          * @param {Roo.bootstrap.UploadCropbox} this
27101          * @param {Object} formData
27102          */
27103         "arrange" : true
27104     });
27105     
27106     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27107 };
27108
27109 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27110     
27111     emptyText : 'Click to upload image',
27112     rotateNotify : 'Image is too small to rotate',
27113     errorTimeout : 3000,
27114     scale : 0,
27115     baseScale : 1,
27116     rotate : 0,
27117     dragable : false,
27118     pinching : false,
27119     mouseX : 0,
27120     mouseY : 0,
27121     cropData : false,
27122     minWidth : 300,
27123     minHeight : 300,
27124     file : false,
27125     exif : {},
27126     baseRotate : 1,
27127     cropType : 'image/jpeg',
27128     buttons : false,
27129     canvasLoaded : false,
27130     isDocument : false,
27131     method : 'POST',
27132     paramName : 'imageUpload',
27133     loadMask : true,
27134     loadingText : 'Loading...',
27135     maskEl : false,
27136     
27137     getAutoCreate : function()
27138     {
27139         var cfg = {
27140             tag : 'div',
27141             cls : 'roo-upload-cropbox',
27142             cn : [
27143                 {
27144                     tag : 'input',
27145                     cls : 'roo-upload-cropbox-selector',
27146                     type : 'file'
27147                 },
27148                 {
27149                     tag : 'div',
27150                     cls : 'roo-upload-cropbox-body',
27151                     style : 'cursor:pointer',
27152                     cn : [
27153                         {
27154                             tag : 'div',
27155                             cls : 'roo-upload-cropbox-preview'
27156                         },
27157                         {
27158                             tag : 'div',
27159                             cls : 'roo-upload-cropbox-thumb'
27160                         },
27161                         {
27162                             tag : 'div',
27163                             cls : 'roo-upload-cropbox-empty-notify',
27164                             html : this.emptyText
27165                         },
27166                         {
27167                             tag : 'div',
27168                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27169                             html : this.rotateNotify
27170                         }
27171                     ]
27172                 },
27173                 {
27174                     tag : 'div',
27175                     cls : 'roo-upload-cropbox-footer',
27176                     cn : {
27177                         tag : 'div',
27178                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27179                         cn : []
27180                     }
27181                 }
27182             ]
27183         };
27184         
27185         return cfg;
27186     },
27187     
27188     onRender : function(ct, position)
27189     {
27190         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27191         
27192         if (this.buttons.length) {
27193             
27194             Roo.each(this.buttons, function(bb) {
27195                 
27196                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27197                 
27198                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27199                 
27200             }, this);
27201         }
27202         
27203         if(this.loadMask){
27204             this.maskEl = this.el;
27205         }
27206     },
27207     
27208     initEvents : function()
27209     {
27210         this.urlAPI = (window.createObjectURL && window) || 
27211                                 (window.URL && URL.revokeObjectURL && URL) || 
27212                                 (window.webkitURL && webkitURL);
27213                         
27214         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27215         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27216         
27217         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27218         this.selectorEl.hide();
27219         
27220         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27221         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27222         
27223         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27224         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27225         this.thumbEl.hide();
27226         
27227         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27228         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27229         
27230         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27231         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27232         this.errorEl.hide();
27233         
27234         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27235         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27236         this.footerEl.hide();
27237         
27238         this.setThumbBoxSize();
27239         
27240         this.bind();
27241         
27242         this.resize();
27243         
27244         this.fireEvent('initial', this);
27245     },
27246
27247     bind : function()
27248     {
27249         var _this = this;
27250         
27251         window.addEventListener("resize", function() { _this.resize(); } );
27252         
27253         this.bodyEl.on('click', this.beforeSelectFile, this);
27254         
27255         if(Roo.isTouch){
27256             this.bodyEl.on('touchstart', this.onTouchStart, this);
27257             this.bodyEl.on('touchmove', this.onTouchMove, this);
27258             this.bodyEl.on('touchend', this.onTouchEnd, this);
27259         }
27260         
27261         if(!Roo.isTouch){
27262             this.bodyEl.on('mousedown', this.onMouseDown, this);
27263             this.bodyEl.on('mousemove', this.onMouseMove, this);
27264             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27265             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27266             Roo.get(document).on('mouseup', this.onMouseUp, this);
27267         }
27268         
27269         this.selectorEl.on('change', this.onFileSelected, this);
27270     },
27271     
27272     reset : function()
27273     {    
27274         this.scale = 0;
27275         this.baseScale = 1;
27276         this.rotate = 0;
27277         this.baseRotate = 1;
27278         this.dragable = false;
27279         this.pinching = false;
27280         this.mouseX = 0;
27281         this.mouseY = 0;
27282         this.cropData = false;
27283         this.notifyEl.dom.innerHTML = this.emptyText;
27284         
27285         this.selectorEl.dom.value = '';
27286         
27287     },
27288     
27289     resize : function()
27290     {
27291         if(this.fireEvent('resize', this) != false){
27292             this.setThumbBoxPosition();
27293             this.setCanvasPosition();
27294         }
27295     },
27296     
27297     onFooterButtonClick : function(e, el, o, type)
27298     {
27299         switch (type) {
27300             case 'rotate-left' :
27301                 this.onRotateLeft(e);
27302                 break;
27303             case 'rotate-right' :
27304                 this.onRotateRight(e);
27305                 break;
27306             case 'picture' :
27307                 this.beforeSelectFile(e);
27308                 break;
27309             case 'trash' :
27310                 this.trash(e);
27311                 break;
27312             case 'crop' :
27313                 this.crop(e);
27314                 break;
27315             case 'download' :
27316                 this.download(e);
27317                 break;
27318             default :
27319                 break;
27320         }
27321         
27322         this.fireEvent('footerbuttonclick', this, type);
27323     },
27324     
27325     beforeSelectFile : function(e)
27326     {
27327         e.preventDefault();
27328         
27329         if(this.fireEvent('beforeselectfile', this) != false){
27330             this.selectorEl.dom.click();
27331         }
27332     },
27333     
27334     onFileSelected : function(e)
27335     {
27336         e.preventDefault();
27337         
27338         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27339             return;
27340         }
27341         
27342         var file = this.selectorEl.dom.files[0];
27343         
27344         if(this.fireEvent('inspect', this, file) != false){
27345             this.prepare(file);
27346         }
27347         
27348     },
27349     
27350     trash : function(e)
27351     {
27352         this.fireEvent('trash', this);
27353     },
27354     
27355     download : function(e)
27356     {
27357         this.fireEvent('download', this);
27358     },
27359     
27360     loadCanvas : function(src)
27361     {   
27362         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27363             
27364             this.reset();
27365             
27366             this.imageEl = document.createElement('img');
27367             
27368             var _this = this;
27369             
27370             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27371             
27372             this.imageEl.src = src;
27373         }
27374     },
27375     
27376     onLoadCanvas : function()
27377     {   
27378         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27379         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27380         
27381         this.bodyEl.un('click', this.beforeSelectFile, this);
27382         
27383         this.notifyEl.hide();
27384         this.thumbEl.show();
27385         this.footerEl.show();
27386         
27387         this.baseRotateLevel();
27388         
27389         if(this.isDocument){
27390             this.setThumbBoxSize();
27391         }
27392         
27393         this.setThumbBoxPosition();
27394         
27395         this.baseScaleLevel();
27396         
27397         this.draw();
27398         
27399         this.resize();
27400         
27401         this.canvasLoaded = true;
27402         
27403         if(this.loadMask){
27404             this.maskEl.unmask();
27405         }
27406         
27407     },
27408     
27409     setCanvasPosition : function()
27410     {   
27411         if(!this.canvasEl){
27412             return;
27413         }
27414         
27415         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27416         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27417         
27418         this.previewEl.setLeft(pw);
27419         this.previewEl.setTop(ph);
27420         
27421     },
27422     
27423     onMouseDown : function(e)
27424     {   
27425         e.stopEvent();
27426         
27427         this.dragable = true;
27428         this.pinching = false;
27429         
27430         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27431             this.dragable = false;
27432             return;
27433         }
27434         
27435         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27436         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27437         
27438     },
27439     
27440     onMouseMove : function(e)
27441     {   
27442         e.stopEvent();
27443         
27444         if(!this.canvasLoaded){
27445             return;
27446         }
27447         
27448         if (!this.dragable){
27449             return;
27450         }
27451         
27452         var minX = Math.ceil(this.thumbEl.getLeft(true));
27453         var minY = Math.ceil(this.thumbEl.getTop(true));
27454         
27455         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27456         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27457         
27458         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27459         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27460         
27461         x = x - this.mouseX;
27462         y = y - this.mouseY;
27463         
27464         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27465         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27466         
27467         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27468         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27469         
27470         this.previewEl.setLeft(bgX);
27471         this.previewEl.setTop(bgY);
27472         
27473         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27474         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27475     },
27476     
27477     onMouseUp : function(e)
27478     {   
27479         e.stopEvent();
27480         
27481         this.dragable = false;
27482     },
27483     
27484     onMouseWheel : function(e)
27485     {   
27486         e.stopEvent();
27487         
27488         this.startScale = this.scale;
27489         
27490         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27491         
27492         if(!this.zoomable()){
27493             this.scale = this.startScale;
27494             return;
27495         }
27496         
27497         this.draw();
27498         
27499         return;
27500     },
27501     
27502     zoomable : function()
27503     {
27504         var minScale = this.thumbEl.getWidth() / this.minWidth;
27505         
27506         if(this.minWidth < this.minHeight){
27507             minScale = this.thumbEl.getHeight() / this.minHeight;
27508         }
27509         
27510         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27511         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27512         
27513         if(
27514                 this.isDocument &&
27515                 (this.rotate == 0 || this.rotate == 180) && 
27516                 (
27517                     width > this.imageEl.OriginWidth || 
27518                     height > this.imageEl.OriginHeight ||
27519                     (width < this.minWidth && height < this.minHeight)
27520                 )
27521         ){
27522             return false;
27523         }
27524         
27525         if(
27526                 this.isDocument &&
27527                 (this.rotate == 90 || this.rotate == 270) && 
27528                 (
27529                     width > this.imageEl.OriginWidth || 
27530                     height > this.imageEl.OriginHeight ||
27531                     (width < this.minHeight && height < this.minWidth)
27532                 )
27533         ){
27534             return false;
27535         }
27536         
27537         if(
27538                 !this.isDocument &&
27539                 (this.rotate == 0 || this.rotate == 180) && 
27540                 (
27541                     width < this.minWidth || 
27542                     width > this.imageEl.OriginWidth || 
27543                     height < this.minHeight || 
27544                     height > this.imageEl.OriginHeight
27545                 )
27546         ){
27547             return false;
27548         }
27549         
27550         if(
27551                 !this.isDocument &&
27552                 (this.rotate == 90 || this.rotate == 270) && 
27553                 (
27554                     width < this.minHeight || 
27555                     width > this.imageEl.OriginWidth || 
27556                     height < this.minWidth || 
27557                     height > this.imageEl.OriginHeight
27558                 )
27559         ){
27560             return false;
27561         }
27562         
27563         return true;
27564         
27565     },
27566     
27567     onRotateLeft : function(e)
27568     {   
27569         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27570             
27571             var minScale = this.thumbEl.getWidth() / this.minWidth;
27572             
27573             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27574             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27575             
27576             this.startScale = this.scale;
27577             
27578             while (this.getScaleLevel() < minScale){
27579             
27580                 this.scale = this.scale + 1;
27581                 
27582                 if(!this.zoomable()){
27583                     break;
27584                 }
27585                 
27586                 if(
27587                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27588                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27589                 ){
27590                     continue;
27591                 }
27592                 
27593                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27594
27595                 this.draw();
27596                 
27597                 return;
27598             }
27599             
27600             this.scale = this.startScale;
27601             
27602             this.onRotateFail();
27603             
27604             return false;
27605         }
27606         
27607         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27608
27609         if(this.isDocument){
27610             this.setThumbBoxSize();
27611             this.setThumbBoxPosition();
27612             this.setCanvasPosition();
27613         }
27614         
27615         this.draw();
27616         
27617         this.fireEvent('rotate', this, 'left');
27618         
27619     },
27620     
27621     onRotateRight : function(e)
27622     {
27623         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27624             
27625             var minScale = this.thumbEl.getWidth() / this.minWidth;
27626         
27627             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27628             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27629             
27630             this.startScale = this.scale;
27631             
27632             while (this.getScaleLevel() < minScale){
27633             
27634                 this.scale = this.scale + 1;
27635                 
27636                 if(!this.zoomable()){
27637                     break;
27638                 }
27639                 
27640                 if(
27641                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27642                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27643                 ){
27644                     continue;
27645                 }
27646                 
27647                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27648
27649                 this.draw();
27650                 
27651                 return;
27652             }
27653             
27654             this.scale = this.startScale;
27655             
27656             this.onRotateFail();
27657             
27658             return false;
27659         }
27660         
27661         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27662
27663         if(this.isDocument){
27664             this.setThumbBoxSize();
27665             this.setThumbBoxPosition();
27666             this.setCanvasPosition();
27667         }
27668         
27669         this.draw();
27670         
27671         this.fireEvent('rotate', this, 'right');
27672     },
27673     
27674     onRotateFail : function()
27675     {
27676         this.errorEl.show(true);
27677         
27678         var _this = this;
27679         
27680         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27681     },
27682     
27683     draw : function()
27684     {
27685         this.previewEl.dom.innerHTML = '';
27686         
27687         var canvasEl = document.createElement("canvas");
27688         
27689         var contextEl = canvasEl.getContext("2d");
27690         
27691         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27692         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27693         var center = this.imageEl.OriginWidth / 2;
27694         
27695         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27696             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27697             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27698             center = this.imageEl.OriginHeight / 2;
27699         }
27700         
27701         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27702         
27703         contextEl.translate(center, center);
27704         contextEl.rotate(this.rotate * Math.PI / 180);
27705
27706         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27707         
27708         this.canvasEl = document.createElement("canvas");
27709         
27710         this.contextEl = this.canvasEl.getContext("2d");
27711         
27712         switch (this.rotate) {
27713             case 0 :
27714                 
27715                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27716                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27717                 
27718                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27719                 
27720                 break;
27721             case 90 : 
27722                 
27723                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27724                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27725                 
27726                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27727                     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);
27728                     break;
27729                 }
27730                 
27731                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27732                 
27733                 break;
27734             case 180 :
27735                 
27736                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27737                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27738                 
27739                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27740                     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);
27741                     break;
27742                 }
27743                 
27744                 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);
27745                 
27746                 break;
27747             case 270 :
27748                 
27749                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27750                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27751         
27752                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27753                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27754                     break;
27755                 }
27756                 
27757                 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);
27758                 
27759                 break;
27760             default : 
27761                 break;
27762         }
27763         
27764         this.previewEl.appendChild(this.canvasEl);
27765         
27766         this.setCanvasPosition();
27767     },
27768     
27769     crop : function()
27770     {
27771         if(!this.canvasLoaded){
27772             return;
27773         }
27774         
27775         var imageCanvas = document.createElement("canvas");
27776         
27777         var imageContext = imageCanvas.getContext("2d");
27778         
27779         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27780         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27781         
27782         var center = imageCanvas.width / 2;
27783         
27784         imageContext.translate(center, center);
27785         
27786         imageContext.rotate(this.rotate * Math.PI / 180);
27787         
27788         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27789         
27790         var canvas = document.createElement("canvas");
27791         
27792         var context = canvas.getContext("2d");
27793                 
27794         canvas.width = this.minWidth;
27795         canvas.height = this.minHeight;
27796
27797         switch (this.rotate) {
27798             case 0 :
27799                 
27800                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27801                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27802                 
27803                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27804                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27805                 
27806                 var targetWidth = this.minWidth - 2 * x;
27807                 var targetHeight = this.minHeight - 2 * y;
27808                 
27809                 var scale = 1;
27810                 
27811                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27812                     scale = targetWidth / width;
27813                 }
27814                 
27815                 if(x > 0 && y == 0){
27816                     scale = targetHeight / height;
27817                 }
27818                 
27819                 if(x > 0 && y > 0){
27820                     scale = targetWidth / width;
27821                     
27822                     if(width < height){
27823                         scale = targetHeight / height;
27824                     }
27825                 }
27826                 
27827                 context.scale(scale, scale);
27828                 
27829                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27830                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27831
27832                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27833                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27834
27835                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27836                 
27837                 break;
27838             case 90 : 
27839                 
27840                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27841                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27842                 
27843                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27844                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27845                 
27846                 var targetWidth = this.minWidth - 2 * x;
27847                 var targetHeight = this.minHeight - 2 * y;
27848                 
27849                 var scale = 1;
27850                 
27851                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27852                     scale = targetWidth / width;
27853                 }
27854                 
27855                 if(x > 0 && y == 0){
27856                     scale = targetHeight / height;
27857                 }
27858                 
27859                 if(x > 0 && y > 0){
27860                     scale = targetWidth / width;
27861                     
27862                     if(width < height){
27863                         scale = targetHeight / height;
27864                     }
27865                 }
27866                 
27867                 context.scale(scale, scale);
27868                 
27869                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27870                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27871
27872                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27873                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27874                 
27875                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27876                 
27877                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27878                 
27879                 break;
27880             case 180 :
27881                 
27882                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27883                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27884                 
27885                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27886                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27887                 
27888                 var targetWidth = this.minWidth - 2 * x;
27889                 var targetHeight = this.minHeight - 2 * y;
27890                 
27891                 var scale = 1;
27892                 
27893                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27894                     scale = targetWidth / width;
27895                 }
27896                 
27897                 if(x > 0 && y == 0){
27898                     scale = targetHeight / height;
27899                 }
27900                 
27901                 if(x > 0 && y > 0){
27902                     scale = targetWidth / width;
27903                     
27904                     if(width < height){
27905                         scale = targetHeight / height;
27906                     }
27907                 }
27908                 
27909                 context.scale(scale, scale);
27910                 
27911                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27912                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27913
27914                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27915                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27916
27917                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27918                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27919                 
27920                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27921                 
27922                 break;
27923             case 270 :
27924                 
27925                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27926                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27927                 
27928                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27929                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27930                 
27931                 var targetWidth = this.minWidth - 2 * x;
27932                 var targetHeight = this.minHeight - 2 * y;
27933                 
27934                 var scale = 1;
27935                 
27936                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27937                     scale = targetWidth / width;
27938                 }
27939                 
27940                 if(x > 0 && y == 0){
27941                     scale = targetHeight / height;
27942                 }
27943                 
27944                 if(x > 0 && y > 0){
27945                     scale = targetWidth / width;
27946                     
27947                     if(width < height){
27948                         scale = targetHeight / height;
27949                     }
27950                 }
27951                 
27952                 context.scale(scale, scale);
27953                 
27954                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27955                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27956
27957                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27958                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27959                 
27960                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27961                 
27962                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27963                 
27964                 break;
27965             default : 
27966                 break;
27967         }
27968         
27969         this.cropData = canvas.toDataURL(this.cropType);
27970         
27971         if(this.fireEvent('crop', this, this.cropData) !== false){
27972             this.process(this.file, this.cropData);
27973         }
27974         
27975         return;
27976         
27977     },
27978     
27979     setThumbBoxSize : function()
27980     {
27981         var width, height;
27982         
27983         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27984             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27985             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27986             
27987             this.minWidth = width;
27988             this.minHeight = height;
27989             
27990             if(this.rotate == 90 || this.rotate == 270){
27991                 this.minWidth = height;
27992                 this.minHeight = width;
27993             }
27994         }
27995         
27996         height = 300;
27997         width = Math.ceil(this.minWidth * height / this.minHeight);
27998         
27999         if(this.minWidth > this.minHeight){
28000             width = 300;
28001             height = Math.ceil(this.minHeight * width / this.minWidth);
28002         }
28003         
28004         this.thumbEl.setStyle({
28005             width : width + 'px',
28006             height : height + 'px'
28007         });
28008
28009         return;
28010             
28011     },
28012     
28013     setThumbBoxPosition : function()
28014     {
28015         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28016         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28017         
28018         this.thumbEl.setLeft(x);
28019         this.thumbEl.setTop(y);
28020         
28021     },
28022     
28023     baseRotateLevel : function()
28024     {
28025         this.baseRotate = 1;
28026         
28027         if(
28028                 typeof(this.exif) != 'undefined' &&
28029                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28030                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28031         ){
28032             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28033         }
28034         
28035         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28036         
28037     },
28038     
28039     baseScaleLevel : function()
28040     {
28041         var width, height;
28042         
28043         if(this.isDocument){
28044             
28045             if(this.baseRotate == 6 || this.baseRotate == 8){
28046             
28047                 height = this.thumbEl.getHeight();
28048                 this.baseScale = height / this.imageEl.OriginWidth;
28049
28050                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28051                     width = this.thumbEl.getWidth();
28052                     this.baseScale = width / this.imageEl.OriginHeight;
28053                 }
28054
28055                 return;
28056             }
28057
28058             height = this.thumbEl.getHeight();
28059             this.baseScale = height / this.imageEl.OriginHeight;
28060
28061             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28062                 width = this.thumbEl.getWidth();
28063                 this.baseScale = width / this.imageEl.OriginWidth;
28064             }
28065
28066             return;
28067         }
28068         
28069         if(this.baseRotate == 6 || this.baseRotate == 8){
28070             
28071             width = this.thumbEl.getHeight();
28072             this.baseScale = width / this.imageEl.OriginHeight;
28073             
28074             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28075                 height = this.thumbEl.getWidth();
28076                 this.baseScale = height / this.imageEl.OriginHeight;
28077             }
28078             
28079             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28080                 height = this.thumbEl.getWidth();
28081                 this.baseScale = height / this.imageEl.OriginHeight;
28082                 
28083                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28084                     width = this.thumbEl.getHeight();
28085                     this.baseScale = width / this.imageEl.OriginWidth;
28086                 }
28087             }
28088             
28089             return;
28090         }
28091         
28092         width = this.thumbEl.getWidth();
28093         this.baseScale = width / this.imageEl.OriginWidth;
28094         
28095         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28096             height = this.thumbEl.getHeight();
28097             this.baseScale = height / this.imageEl.OriginHeight;
28098         }
28099         
28100         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28101             
28102             height = this.thumbEl.getHeight();
28103             this.baseScale = height / this.imageEl.OriginHeight;
28104             
28105             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28106                 width = this.thumbEl.getWidth();
28107                 this.baseScale = width / this.imageEl.OriginWidth;
28108             }
28109             
28110         }
28111         
28112         return;
28113     },
28114     
28115     getScaleLevel : function()
28116     {
28117         return this.baseScale * Math.pow(1.1, this.scale);
28118     },
28119     
28120     onTouchStart : function(e)
28121     {
28122         if(!this.canvasLoaded){
28123             this.beforeSelectFile(e);
28124             return;
28125         }
28126         
28127         var touches = e.browserEvent.touches;
28128         
28129         if(!touches){
28130             return;
28131         }
28132         
28133         if(touches.length == 1){
28134             this.onMouseDown(e);
28135             return;
28136         }
28137         
28138         if(touches.length != 2){
28139             return;
28140         }
28141         
28142         var coords = [];
28143         
28144         for(var i = 0, finger; finger = touches[i]; i++){
28145             coords.push(finger.pageX, finger.pageY);
28146         }
28147         
28148         var x = Math.pow(coords[0] - coords[2], 2);
28149         var y = Math.pow(coords[1] - coords[3], 2);
28150         
28151         this.startDistance = Math.sqrt(x + y);
28152         
28153         this.startScale = this.scale;
28154         
28155         this.pinching = true;
28156         this.dragable = false;
28157         
28158     },
28159     
28160     onTouchMove : function(e)
28161     {
28162         if(!this.pinching && !this.dragable){
28163             return;
28164         }
28165         
28166         var touches = e.browserEvent.touches;
28167         
28168         if(!touches){
28169             return;
28170         }
28171         
28172         if(this.dragable){
28173             this.onMouseMove(e);
28174             return;
28175         }
28176         
28177         var coords = [];
28178         
28179         for(var i = 0, finger; finger = touches[i]; i++){
28180             coords.push(finger.pageX, finger.pageY);
28181         }
28182         
28183         var x = Math.pow(coords[0] - coords[2], 2);
28184         var y = Math.pow(coords[1] - coords[3], 2);
28185         
28186         this.endDistance = Math.sqrt(x + y);
28187         
28188         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28189         
28190         if(!this.zoomable()){
28191             this.scale = this.startScale;
28192             return;
28193         }
28194         
28195         this.draw();
28196         
28197     },
28198     
28199     onTouchEnd : function(e)
28200     {
28201         this.pinching = false;
28202         this.dragable = false;
28203         
28204     },
28205     
28206     process : function(file, crop)
28207     {
28208         if(this.loadMask){
28209             this.maskEl.mask(this.loadingText);
28210         }
28211         
28212         this.xhr = new XMLHttpRequest();
28213         
28214         file.xhr = this.xhr;
28215
28216         this.xhr.open(this.method, this.url, true);
28217         
28218         var headers = {
28219             "Accept": "application/json",
28220             "Cache-Control": "no-cache",
28221             "X-Requested-With": "XMLHttpRequest"
28222         };
28223         
28224         for (var headerName in headers) {
28225             var headerValue = headers[headerName];
28226             if (headerValue) {
28227                 this.xhr.setRequestHeader(headerName, headerValue);
28228             }
28229         }
28230         
28231         var _this = this;
28232         
28233         this.xhr.onload = function()
28234         {
28235             _this.xhrOnLoad(_this.xhr);
28236         }
28237         
28238         this.xhr.onerror = function()
28239         {
28240             _this.xhrOnError(_this.xhr);
28241         }
28242         
28243         var formData = new FormData();
28244
28245         formData.append('returnHTML', 'NO');
28246         
28247         if(crop){
28248             formData.append('crop', crop);
28249         }
28250         
28251         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28252             formData.append(this.paramName, file, file.name);
28253         }
28254         
28255         if(typeof(file.filename) != 'undefined'){
28256             formData.append('filename', file.filename);
28257         }
28258         
28259         if(typeof(file.mimetype) != 'undefined'){
28260             formData.append('mimetype', file.mimetype);
28261         }
28262         
28263         if(this.fireEvent('arrange', this, formData) != false){
28264             this.xhr.send(formData);
28265         };
28266     },
28267     
28268     xhrOnLoad : function(xhr)
28269     {
28270         if(this.loadMask){
28271             this.maskEl.unmask();
28272         }
28273         
28274         if (xhr.readyState !== 4) {
28275             this.fireEvent('exception', this, xhr);
28276             return;
28277         }
28278
28279         var response = Roo.decode(xhr.responseText);
28280         
28281         if(!response.success){
28282             this.fireEvent('exception', this, xhr);
28283             return;
28284         }
28285         
28286         var response = Roo.decode(xhr.responseText);
28287         
28288         this.fireEvent('upload', this, response);
28289         
28290     },
28291     
28292     xhrOnError : function()
28293     {
28294         if(this.loadMask){
28295             this.maskEl.unmask();
28296         }
28297         
28298         Roo.log('xhr on error');
28299         
28300         var response = Roo.decode(xhr.responseText);
28301           
28302         Roo.log(response);
28303         
28304     },
28305     
28306     prepare : function(file)
28307     {   
28308         if(this.loadMask){
28309             this.maskEl.mask(this.loadingText);
28310         }
28311         
28312         this.file = false;
28313         this.exif = {};
28314         
28315         if(typeof(file) === 'string'){
28316             this.loadCanvas(file);
28317             return;
28318         }
28319         
28320         if(!file || !this.urlAPI){
28321             return;
28322         }
28323         
28324         this.file = file;
28325         this.cropType = file.type;
28326         
28327         var _this = this;
28328         
28329         if(this.fireEvent('prepare', this, this.file) != false){
28330             
28331             var reader = new FileReader();
28332             
28333             reader.onload = function (e) {
28334                 if (e.target.error) {
28335                     Roo.log(e.target.error);
28336                     return;
28337                 }
28338                 
28339                 var buffer = e.target.result,
28340                     dataView = new DataView(buffer),
28341                     offset = 2,
28342                     maxOffset = dataView.byteLength - 4,
28343                     markerBytes,
28344                     markerLength;
28345                 
28346                 if (dataView.getUint16(0) === 0xffd8) {
28347                     while (offset < maxOffset) {
28348                         markerBytes = dataView.getUint16(offset);
28349                         
28350                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28351                             markerLength = dataView.getUint16(offset + 2) + 2;
28352                             if (offset + markerLength > dataView.byteLength) {
28353                                 Roo.log('Invalid meta data: Invalid segment size.');
28354                                 break;
28355                             }
28356                             
28357                             if(markerBytes == 0xffe1){
28358                                 _this.parseExifData(
28359                                     dataView,
28360                                     offset,
28361                                     markerLength
28362                                 );
28363                             }
28364                             
28365                             offset += markerLength;
28366                             
28367                             continue;
28368                         }
28369                         
28370                         break;
28371                     }
28372                     
28373                 }
28374                 
28375                 var url = _this.urlAPI.createObjectURL(_this.file);
28376                 
28377                 _this.loadCanvas(url);
28378                 
28379                 return;
28380             }
28381             
28382             reader.readAsArrayBuffer(this.file);
28383             
28384         }
28385         
28386     },
28387     
28388     parseExifData : function(dataView, offset, length)
28389     {
28390         var tiffOffset = offset + 10,
28391             littleEndian,
28392             dirOffset;
28393     
28394         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28395             // No Exif data, might be XMP data instead
28396             return;
28397         }
28398         
28399         // Check for the ASCII code for "Exif" (0x45786966):
28400         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28401             // No Exif data, might be XMP data instead
28402             return;
28403         }
28404         if (tiffOffset + 8 > dataView.byteLength) {
28405             Roo.log('Invalid Exif data: Invalid segment size.');
28406             return;
28407         }
28408         // Check for the two null bytes:
28409         if (dataView.getUint16(offset + 8) !== 0x0000) {
28410             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28411             return;
28412         }
28413         // Check the byte alignment:
28414         switch (dataView.getUint16(tiffOffset)) {
28415         case 0x4949:
28416             littleEndian = true;
28417             break;
28418         case 0x4D4D:
28419             littleEndian = false;
28420             break;
28421         default:
28422             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28423             return;
28424         }
28425         // Check for the TIFF tag marker (0x002A):
28426         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28427             Roo.log('Invalid Exif data: Missing TIFF marker.');
28428             return;
28429         }
28430         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28431         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28432         
28433         this.parseExifTags(
28434             dataView,
28435             tiffOffset,
28436             tiffOffset + dirOffset,
28437             littleEndian
28438         );
28439     },
28440     
28441     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28442     {
28443         var tagsNumber,
28444             dirEndOffset,
28445             i;
28446         if (dirOffset + 6 > dataView.byteLength) {
28447             Roo.log('Invalid Exif data: Invalid directory offset.');
28448             return;
28449         }
28450         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28451         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28452         if (dirEndOffset + 4 > dataView.byteLength) {
28453             Roo.log('Invalid Exif data: Invalid directory size.');
28454             return;
28455         }
28456         for (i = 0; i < tagsNumber; i += 1) {
28457             this.parseExifTag(
28458                 dataView,
28459                 tiffOffset,
28460                 dirOffset + 2 + 12 * i, // tag offset
28461                 littleEndian
28462             );
28463         }
28464         // Return the offset to the next directory:
28465         return dataView.getUint32(dirEndOffset, littleEndian);
28466     },
28467     
28468     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28469     {
28470         var tag = dataView.getUint16(offset, littleEndian);
28471         
28472         this.exif[tag] = this.getExifValue(
28473             dataView,
28474             tiffOffset,
28475             offset,
28476             dataView.getUint16(offset + 2, littleEndian), // tag type
28477             dataView.getUint32(offset + 4, littleEndian), // tag length
28478             littleEndian
28479         );
28480     },
28481     
28482     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28483     {
28484         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28485             tagSize,
28486             dataOffset,
28487             values,
28488             i,
28489             str,
28490             c;
28491     
28492         if (!tagType) {
28493             Roo.log('Invalid Exif data: Invalid tag type.');
28494             return;
28495         }
28496         
28497         tagSize = tagType.size * length;
28498         // Determine if the value is contained in the dataOffset bytes,
28499         // or if the value at the dataOffset is a pointer to the actual data:
28500         dataOffset = tagSize > 4 ?
28501                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28502         if (dataOffset + tagSize > dataView.byteLength) {
28503             Roo.log('Invalid Exif data: Invalid data offset.');
28504             return;
28505         }
28506         if (length === 1) {
28507             return tagType.getValue(dataView, dataOffset, littleEndian);
28508         }
28509         values = [];
28510         for (i = 0; i < length; i += 1) {
28511             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28512         }
28513         
28514         if (tagType.ascii) {
28515             str = '';
28516             // Concatenate the chars:
28517             for (i = 0; i < values.length; i += 1) {
28518                 c = values[i];
28519                 // Ignore the terminating NULL byte(s):
28520                 if (c === '\u0000') {
28521                     break;
28522                 }
28523                 str += c;
28524             }
28525             return str;
28526         }
28527         return values;
28528     }
28529     
28530 });
28531
28532 Roo.apply(Roo.bootstrap.UploadCropbox, {
28533     tags : {
28534         'Orientation': 0x0112
28535     },
28536     
28537     Orientation: {
28538             1: 0, //'top-left',
28539 //            2: 'top-right',
28540             3: 180, //'bottom-right',
28541 //            4: 'bottom-left',
28542 //            5: 'left-top',
28543             6: 90, //'right-top',
28544 //            7: 'right-bottom',
28545             8: 270 //'left-bottom'
28546     },
28547     
28548     exifTagTypes : {
28549         // byte, 8-bit unsigned int:
28550         1: {
28551             getValue: function (dataView, dataOffset) {
28552                 return dataView.getUint8(dataOffset);
28553             },
28554             size: 1
28555         },
28556         // ascii, 8-bit byte:
28557         2: {
28558             getValue: function (dataView, dataOffset) {
28559                 return String.fromCharCode(dataView.getUint8(dataOffset));
28560             },
28561             size: 1,
28562             ascii: true
28563         },
28564         // short, 16 bit int:
28565         3: {
28566             getValue: function (dataView, dataOffset, littleEndian) {
28567                 return dataView.getUint16(dataOffset, littleEndian);
28568             },
28569             size: 2
28570         },
28571         // long, 32 bit int:
28572         4: {
28573             getValue: function (dataView, dataOffset, littleEndian) {
28574                 return dataView.getUint32(dataOffset, littleEndian);
28575             },
28576             size: 4
28577         },
28578         // rational = two long values, first is numerator, second is denominator:
28579         5: {
28580             getValue: function (dataView, dataOffset, littleEndian) {
28581                 return dataView.getUint32(dataOffset, littleEndian) /
28582                     dataView.getUint32(dataOffset + 4, littleEndian);
28583             },
28584             size: 8
28585         },
28586         // slong, 32 bit signed int:
28587         9: {
28588             getValue: function (dataView, dataOffset, littleEndian) {
28589                 return dataView.getInt32(dataOffset, littleEndian);
28590             },
28591             size: 4
28592         },
28593         // srational, two slongs, first is numerator, second is denominator:
28594         10: {
28595             getValue: function (dataView, dataOffset, littleEndian) {
28596                 return dataView.getInt32(dataOffset, littleEndian) /
28597                     dataView.getInt32(dataOffset + 4, littleEndian);
28598             },
28599             size: 8
28600         }
28601     },
28602     
28603     footer : {
28604         STANDARD : [
28605             {
28606                 tag : 'div',
28607                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28608                 action : 'rotate-left',
28609                 cn : [
28610                     {
28611                         tag : 'button',
28612                         cls : 'btn btn-default',
28613                         html : '<i class="fa fa-undo"></i>'
28614                     }
28615                 ]
28616             },
28617             {
28618                 tag : 'div',
28619                 cls : 'btn-group roo-upload-cropbox-picture',
28620                 action : 'picture',
28621                 cn : [
28622                     {
28623                         tag : 'button',
28624                         cls : 'btn btn-default',
28625                         html : '<i class="fa fa-picture-o"></i>'
28626                     }
28627                 ]
28628             },
28629             {
28630                 tag : 'div',
28631                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28632                 action : 'rotate-right',
28633                 cn : [
28634                     {
28635                         tag : 'button',
28636                         cls : 'btn btn-default',
28637                         html : '<i class="fa fa-repeat"></i>'
28638                     }
28639                 ]
28640             }
28641         ],
28642         DOCUMENT : [
28643             {
28644                 tag : 'div',
28645                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28646                 action : 'rotate-left',
28647                 cn : [
28648                     {
28649                         tag : 'button',
28650                         cls : 'btn btn-default',
28651                         html : '<i class="fa fa-undo"></i>'
28652                     }
28653                 ]
28654             },
28655             {
28656                 tag : 'div',
28657                 cls : 'btn-group roo-upload-cropbox-download',
28658                 action : 'download',
28659                 cn : [
28660                     {
28661                         tag : 'button',
28662                         cls : 'btn btn-default',
28663                         html : '<i class="fa fa-download"></i>'
28664                     }
28665                 ]
28666             },
28667             {
28668                 tag : 'div',
28669                 cls : 'btn-group roo-upload-cropbox-crop',
28670                 action : 'crop',
28671                 cn : [
28672                     {
28673                         tag : 'button',
28674                         cls : 'btn btn-default',
28675                         html : '<i class="fa fa-crop"></i>'
28676                     }
28677                 ]
28678             },
28679             {
28680                 tag : 'div',
28681                 cls : 'btn-group roo-upload-cropbox-trash',
28682                 action : 'trash',
28683                 cn : [
28684                     {
28685                         tag : 'button',
28686                         cls : 'btn btn-default',
28687                         html : '<i class="fa fa-trash"></i>'
28688                     }
28689                 ]
28690             },
28691             {
28692                 tag : 'div',
28693                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28694                 action : 'rotate-right',
28695                 cn : [
28696                     {
28697                         tag : 'button',
28698                         cls : 'btn btn-default',
28699                         html : '<i class="fa fa-repeat"></i>'
28700                     }
28701                 ]
28702             }
28703         ],
28704         ROTATOR : [
28705             {
28706                 tag : 'div',
28707                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28708                 action : 'rotate-left',
28709                 cn : [
28710                     {
28711                         tag : 'button',
28712                         cls : 'btn btn-default',
28713                         html : '<i class="fa fa-undo"></i>'
28714                     }
28715                 ]
28716             },
28717             {
28718                 tag : 'div',
28719                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28720                 action : 'rotate-right',
28721                 cn : [
28722                     {
28723                         tag : 'button',
28724                         cls : 'btn btn-default',
28725                         html : '<i class="fa fa-repeat"></i>'
28726                     }
28727                 ]
28728             }
28729         ]
28730     }
28731 });
28732
28733 /*
28734 * Licence: LGPL
28735 */
28736
28737 /**
28738  * @class Roo.bootstrap.DocumentManager
28739  * @extends Roo.bootstrap.Component
28740  * Bootstrap DocumentManager class
28741  * @cfg {String} paramName default 'imageUpload'
28742  * @cfg {String} toolTipName default 'filename'
28743  * @cfg {String} method default POST
28744  * @cfg {String} url action url
28745  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28746  * @cfg {Boolean} multiple multiple upload default true
28747  * @cfg {Number} thumbSize default 300
28748  * @cfg {String} fieldLabel
28749  * @cfg {Number} labelWidth default 4
28750  * @cfg {String} labelAlign (left|top) default left
28751  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28752 * @cfg {Number} labellg set the width of label (1-12)
28753  * @cfg {Number} labelmd set the width of label (1-12)
28754  * @cfg {Number} labelsm set the width of label (1-12)
28755  * @cfg {Number} labelxs set the width of label (1-12)
28756  * 
28757  * @constructor
28758  * Create a new DocumentManager
28759  * @param {Object} config The config object
28760  */
28761
28762 Roo.bootstrap.DocumentManager = function(config){
28763     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28764     
28765     this.files = [];
28766     this.delegates = [];
28767     
28768     this.addEvents({
28769         /**
28770          * @event initial
28771          * Fire when initial the DocumentManager
28772          * @param {Roo.bootstrap.DocumentManager} this
28773          */
28774         "initial" : true,
28775         /**
28776          * @event inspect
28777          * inspect selected file
28778          * @param {Roo.bootstrap.DocumentManager} this
28779          * @param {File} file
28780          */
28781         "inspect" : true,
28782         /**
28783          * @event exception
28784          * Fire when xhr load exception
28785          * @param {Roo.bootstrap.DocumentManager} this
28786          * @param {XMLHttpRequest} xhr
28787          */
28788         "exception" : true,
28789         /**
28790          * @event afterupload
28791          * Fire when xhr load exception
28792          * @param {Roo.bootstrap.DocumentManager} this
28793          * @param {XMLHttpRequest} xhr
28794          */
28795         "afterupload" : true,
28796         /**
28797          * @event prepare
28798          * prepare the form data
28799          * @param {Roo.bootstrap.DocumentManager} this
28800          * @param {Object} formData
28801          */
28802         "prepare" : true,
28803         /**
28804          * @event remove
28805          * Fire when remove the file
28806          * @param {Roo.bootstrap.DocumentManager} this
28807          * @param {Object} file
28808          */
28809         "remove" : true,
28810         /**
28811          * @event refresh
28812          * Fire after refresh the file
28813          * @param {Roo.bootstrap.DocumentManager} this
28814          */
28815         "refresh" : true,
28816         /**
28817          * @event click
28818          * Fire after click the image
28819          * @param {Roo.bootstrap.DocumentManager} this
28820          * @param {Object} file
28821          */
28822         "click" : true,
28823         /**
28824          * @event edit
28825          * Fire when upload a image and editable set to true
28826          * @param {Roo.bootstrap.DocumentManager} this
28827          * @param {Object} file
28828          */
28829         "edit" : true,
28830         /**
28831          * @event beforeselectfile
28832          * Fire before select file
28833          * @param {Roo.bootstrap.DocumentManager} this
28834          */
28835         "beforeselectfile" : true,
28836         /**
28837          * @event process
28838          * Fire before process file
28839          * @param {Roo.bootstrap.DocumentManager} this
28840          * @param {Object} file
28841          */
28842         "process" : true,
28843         /**
28844          * @event previewrendered
28845          * Fire when preview rendered
28846          * @param {Roo.bootstrap.DocumentManager} this
28847          * @param {Object} file
28848          */
28849         "previewrendered" : true,
28850         /**
28851          */
28852         "previewResize" : true
28853         
28854     });
28855 };
28856
28857 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28858     
28859     boxes : 0,
28860     inputName : '',
28861     thumbSize : 300,
28862     multiple : true,
28863     files : false,
28864     method : 'POST',
28865     url : '',
28866     paramName : 'imageUpload',
28867     toolTipName : 'filename',
28868     fieldLabel : '',
28869     labelWidth : 4,
28870     labelAlign : 'left',
28871     editable : true,
28872     delegates : false,
28873     xhr : false, 
28874     
28875     labellg : 0,
28876     labelmd : 0,
28877     labelsm : 0,
28878     labelxs : 0,
28879     
28880     getAutoCreate : function()
28881     {   
28882         var managerWidget = {
28883             tag : 'div',
28884             cls : 'roo-document-manager',
28885             cn : [
28886                 {
28887                     tag : 'input',
28888                     cls : 'roo-document-manager-selector',
28889                     type : 'file'
28890                 },
28891                 {
28892                     tag : 'div',
28893                     cls : 'roo-document-manager-uploader',
28894                     cn : [
28895                         {
28896                             tag : 'div',
28897                             cls : 'roo-document-manager-upload-btn',
28898                             html : '<i class="fa fa-plus"></i>'
28899                         }
28900                     ]
28901                     
28902                 }
28903             ]
28904         };
28905         
28906         var content = [
28907             {
28908                 tag : 'div',
28909                 cls : 'column col-md-12',
28910                 cn : managerWidget
28911             }
28912         ];
28913         
28914         if(this.fieldLabel.length){
28915             
28916             content = [
28917                 {
28918                     tag : 'div',
28919                     cls : 'column col-md-12',
28920                     html : this.fieldLabel
28921                 },
28922                 {
28923                     tag : 'div',
28924                     cls : 'column col-md-12',
28925                     cn : managerWidget
28926                 }
28927             ];
28928
28929             if(this.labelAlign == 'left'){
28930                 content = [
28931                     {
28932                         tag : 'div',
28933                         cls : 'column',
28934                         html : this.fieldLabel
28935                     },
28936                     {
28937                         tag : 'div',
28938                         cls : 'column',
28939                         cn : managerWidget
28940                     }
28941                 ];
28942                 
28943                 if(this.labelWidth > 12){
28944                     content[0].style = "width: " + this.labelWidth + 'px';
28945                 }
28946
28947                 if(this.labelWidth < 13 && this.labelmd == 0){
28948                     this.labelmd = this.labelWidth;
28949                 }
28950
28951                 if(this.labellg > 0){
28952                     content[0].cls += ' col-lg-' + this.labellg;
28953                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28954                 }
28955
28956                 if(this.labelmd > 0){
28957                     content[0].cls += ' col-md-' + this.labelmd;
28958                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28959                 }
28960
28961                 if(this.labelsm > 0){
28962                     content[0].cls += ' col-sm-' + this.labelsm;
28963                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28964                 }
28965
28966                 if(this.labelxs > 0){
28967                     content[0].cls += ' col-xs-' + this.labelxs;
28968                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28969                 }
28970                 
28971             }
28972         }
28973         
28974         var cfg = {
28975             tag : 'div',
28976             cls : 'row clearfix',
28977             cn : content
28978         };
28979         
28980         return cfg;
28981         
28982     },
28983     
28984     initEvents : function()
28985     {
28986         this.managerEl = this.el.select('.roo-document-manager', true).first();
28987         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28988         
28989         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28990         this.selectorEl.hide();
28991         
28992         if(this.multiple){
28993             this.selectorEl.attr('multiple', 'multiple');
28994         }
28995         
28996         this.selectorEl.on('change', this.onFileSelected, this);
28997         
28998         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28999         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29000         
29001         this.uploader.on('click', this.onUploaderClick, this);
29002         
29003         this.renderProgressDialog();
29004         
29005         var _this = this;
29006         
29007         window.addEventListener("resize", function() { _this.refresh(); } );
29008         
29009         this.fireEvent('initial', this);
29010     },
29011     
29012     renderProgressDialog : function()
29013     {
29014         var _this = this;
29015         
29016         this.progressDialog = new Roo.bootstrap.Modal({
29017             cls : 'roo-document-manager-progress-dialog',
29018             allow_close : false,
29019             title : '',
29020             buttons : [
29021                 {
29022                     name  :'cancel',
29023                     weight : 'danger',
29024                     html : 'Cancel'
29025                 }
29026             ], 
29027             listeners : { 
29028                 btnclick : function() {
29029                     _this.uploadCancel();
29030                     this.hide();
29031                 }
29032             }
29033         });
29034          
29035         this.progressDialog.render(Roo.get(document.body));
29036          
29037         this.progress = new Roo.bootstrap.Progress({
29038             cls : 'roo-document-manager-progress',
29039             active : true,
29040             striped : true
29041         });
29042         
29043         this.progress.render(this.progressDialog.getChildContainer());
29044         
29045         this.progressBar = new Roo.bootstrap.ProgressBar({
29046             cls : 'roo-document-manager-progress-bar',
29047             aria_valuenow : 0,
29048             aria_valuemin : 0,
29049             aria_valuemax : 12,
29050             panel : 'success'
29051         });
29052         
29053         this.progressBar.render(this.progress.getChildContainer());
29054     },
29055     
29056     onUploaderClick : function(e)
29057     {
29058         e.preventDefault();
29059      
29060         if(this.fireEvent('beforeselectfile', this) != false){
29061             this.selectorEl.dom.click();
29062         }
29063         
29064     },
29065     
29066     onFileSelected : function(e)
29067     {
29068         e.preventDefault();
29069         
29070         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29071             return;
29072         }
29073         
29074         Roo.each(this.selectorEl.dom.files, function(file){
29075             if(this.fireEvent('inspect', this, file) != false){
29076                 this.files.push(file);
29077             }
29078         }, this);
29079         
29080         this.queue();
29081         
29082     },
29083     
29084     queue : function()
29085     {
29086         this.selectorEl.dom.value = '';
29087         
29088         if(!this.files || !this.files.length){
29089             return;
29090         }
29091         
29092         if(this.boxes > 0 && this.files.length > this.boxes){
29093             this.files = this.files.slice(0, this.boxes);
29094         }
29095         
29096         this.uploader.show();
29097         
29098         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29099             this.uploader.hide();
29100         }
29101         
29102         var _this = this;
29103         
29104         var files = [];
29105         
29106         var docs = [];
29107         
29108         Roo.each(this.files, function(file){
29109             
29110             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29111                 var f = this.renderPreview(file);
29112                 files.push(f);
29113                 return;
29114             }
29115             
29116             if(file.type.indexOf('image') != -1){
29117                 this.delegates.push(
29118                     (function(){
29119                         _this.process(file);
29120                     }).createDelegate(this)
29121                 );
29122         
29123                 return;
29124             }
29125             
29126             docs.push(
29127                 (function(){
29128                     _this.process(file);
29129                 }).createDelegate(this)
29130             );
29131             
29132         }, this);
29133         
29134         this.files = files;
29135         
29136         this.delegates = this.delegates.concat(docs);
29137         
29138         if(!this.delegates.length){
29139             this.refresh();
29140             return;
29141         }
29142         
29143         this.progressBar.aria_valuemax = this.delegates.length;
29144         
29145         this.arrange();
29146         
29147         return;
29148     },
29149     
29150     arrange : function()
29151     {
29152         if(!this.delegates.length){
29153             this.progressDialog.hide();
29154             this.refresh();
29155             return;
29156         }
29157         
29158         var delegate = this.delegates.shift();
29159         
29160         this.progressDialog.show();
29161         
29162         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29163         
29164         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29165         
29166         delegate();
29167     },
29168     
29169     refresh : function()
29170     {
29171         this.uploader.show();
29172         
29173         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29174             this.uploader.hide();
29175         }
29176         
29177         Roo.isTouch ? this.closable(false) : this.closable(true);
29178         
29179         this.fireEvent('refresh', this);
29180     },
29181     
29182     onRemove : function(e, el, o)
29183     {
29184         e.preventDefault();
29185         
29186         this.fireEvent('remove', this, o);
29187         
29188     },
29189     
29190     remove : function(o)
29191     {
29192         var files = [];
29193         
29194         Roo.each(this.files, function(file){
29195             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29196                 files.push(file);
29197                 return;
29198             }
29199
29200             o.target.remove();
29201
29202         }, this);
29203         
29204         this.files = files;
29205         
29206         this.refresh();
29207     },
29208     
29209     clear : function()
29210     {
29211         Roo.each(this.files, function(file){
29212             if(!file.target){
29213                 return;
29214             }
29215             
29216             file.target.remove();
29217
29218         }, this);
29219         
29220         this.files = [];
29221         
29222         this.refresh();
29223     },
29224     
29225     onClick : function(e, el, o)
29226     {
29227         e.preventDefault();
29228         
29229         this.fireEvent('click', this, o);
29230         
29231     },
29232     
29233     closable : function(closable)
29234     {
29235         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29236             
29237             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29238             
29239             if(closable){
29240                 el.show();
29241                 return;
29242             }
29243             
29244             el.hide();
29245             
29246         }, this);
29247     },
29248     
29249     xhrOnLoad : function(xhr)
29250     {
29251         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29252             el.remove();
29253         }, this);
29254         
29255         if (xhr.readyState !== 4) {
29256             this.arrange();
29257             this.fireEvent('exception', this, xhr);
29258             return;
29259         }
29260
29261         var response = Roo.decode(xhr.responseText);
29262         
29263         if(!response.success){
29264             this.arrange();
29265             this.fireEvent('exception', this, xhr);
29266             return;
29267         }
29268         
29269         var file = this.renderPreview(response.data);
29270         
29271         this.files.push(file);
29272         
29273         this.arrange();
29274         
29275         this.fireEvent('afterupload', this, xhr);
29276         
29277     },
29278     
29279     xhrOnError : function(xhr)
29280     {
29281         Roo.log('xhr on error');
29282         
29283         var response = Roo.decode(xhr.responseText);
29284           
29285         Roo.log(response);
29286         
29287         this.arrange();
29288     },
29289     
29290     process : function(file)
29291     {
29292         if(this.fireEvent('process', this, file) !== false){
29293             if(this.editable && file.type.indexOf('image') != -1){
29294                 this.fireEvent('edit', this, file);
29295                 return;
29296             }
29297
29298             this.uploadStart(file, false);
29299
29300             return;
29301         }
29302         
29303     },
29304     
29305     uploadStart : function(file, crop)
29306     {
29307         this.xhr = new XMLHttpRequest();
29308         
29309         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29310             this.arrange();
29311             return;
29312         }
29313         
29314         file.xhr = this.xhr;
29315             
29316         this.managerEl.createChild({
29317             tag : 'div',
29318             cls : 'roo-document-manager-loading',
29319             cn : [
29320                 {
29321                     tag : 'div',
29322                     tooltip : file.name,
29323                     cls : 'roo-document-manager-thumb',
29324                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29325                 }
29326             ]
29327
29328         });
29329
29330         this.xhr.open(this.method, this.url, true);
29331         
29332         var headers = {
29333             "Accept": "application/json",
29334             "Cache-Control": "no-cache",
29335             "X-Requested-With": "XMLHttpRequest"
29336         };
29337         
29338         for (var headerName in headers) {
29339             var headerValue = headers[headerName];
29340             if (headerValue) {
29341                 this.xhr.setRequestHeader(headerName, headerValue);
29342             }
29343         }
29344         
29345         var _this = this;
29346         
29347         this.xhr.onload = function()
29348         {
29349             _this.xhrOnLoad(_this.xhr);
29350         }
29351         
29352         this.xhr.onerror = function()
29353         {
29354             _this.xhrOnError(_this.xhr);
29355         }
29356         
29357         var formData = new FormData();
29358
29359         formData.append('returnHTML', 'NO');
29360         
29361         if(crop){
29362             formData.append('crop', crop);
29363         }
29364         
29365         formData.append(this.paramName, file, file.name);
29366         
29367         var options = {
29368             file : file, 
29369             manually : false
29370         };
29371         
29372         if(this.fireEvent('prepare', this, formData, options) != false){
29373             
29374             if(options.manually){
29375                 return;
29376             }
29377             
29378             this.xhr.send(formData);
29379             return;
29380         };
29381         
29382         this.uploadCancel();
29383     },
29384     
29385     uploadCancel : function()
29386     {
29387         if (this.xhr) {
29388             this.xhr.abort();
29389         }
29390         
29391         this.delegates = [];
29392         
29393         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29394             el.remove();
29395         }, this);
29396         
29397         this.arrange();
29398     },
29399     
29400     renderPreview : function(file)
29401     {
29402         if(typeof(file.target) != 'undefined' && file.target){
29403             return file;
29404         }
29405         
29406         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29407         
29408         var previewEl = this.managerEl.createChild({
29409             tag : 'div',
29410             cls : 'roo-document-manager-preview',
29411             cn : [
29412                 {
29413                     tag : 'div',
29414                     tooltip : file[this.toolTipName],
29415                     cls : 'roo-document-manager-thumb',
29416                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29417                 },
29418                 {
29419                     tag : 'button',
29420                     cls : 'close',
29421                     html : '<i class="fa fa-times-circle"></i>'
29422                 }
29423             ]
29424         });
29425
29426         var close = previewEl.select('button.close', true).first();
29427
29428         close.on('click', this.onRemove, this, file);
29429
29430         file.target = previewEl;
29431
29432         var image = previewEl.select('img', true).first();
29433         
29434         var _this = this;
29435         
29436         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29437         
29438         image.on('click', this.onClick, this, file);
29439         
29440         this.fireEvent('previewrendered', this, file);
29441         
29442         return file;
29443         
29444     },
29445     
29446     onPreviewLoad : function(file, image)
29447     {
29448         if(typeof(file.target) == 'undefined' || !file.target){
29449             return;
29450         }
29451         
29452         var width = image.dom.naturalWidth || image.dom.width;
29453         var height = image.dom.naturalHeight || image.dom.height;
29454         
29455         if(!this.previewResize) {
29456             return;
29457         }
29458         
29459         if(width > height){
29460             file.target.addClass('wide');
29461             return;
29462         }
29463         
29464         file.target.addClass('tall');
29465         return;
29466         
29467     },
29468     
29469     uploadFromSource : function(file, crop)
29470     {
29471         this.xhr = new XMLHttpRequest();
29472         
29473         this.managerEl.createChild({
29474             tag : 'div',
29475             cls : 'roo-document-manager-loading',
29476             cn : [
29477                 {
29478                     tag : 'div',
29479                     tooltip : file.name,
29480                     cls : 'roo-document-manager-thumb',
29481                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29482                 }
29483             ]
29484
29485         });
29486
29487         this.xhr.open(this.method, this.url, true);
29488         
29489         var headers = {
29490             "Accept": "application/json",
29491             "Cache-Control": "no-cache",
29492             "X-Requested-With": "XMLHttpRequest"
29493         };
29494         
29495         for (var headerName in headers) {
29496             var headerValue = headers[headerName];
29497             if (headerValue) {
29498                 this.xhr.setRequestHeader(headerName, headerValue);
29499             }
29500         }
29501         
29502         var _this = this;
29503         
29504         this.xhr.onload = function()
29505         {
29506             _this.xhrOnLoad(_this.xhr);
29507         }
29508         
29509         this.xhr.onerror = function()
29510         {
29511             _this.xhrOnError(_this.xhr);
29512         }
29513         
29514         var formData = new FormData();
29515
29516         formData.append('returnHTML', 'NO');
29517         
29518         formData.append('crop', crop);
29519         
29520         if(typeof(file.filename) != 'undefined'){
29521             formData.append('filename', file.filename);
29522         }
29523         
29524         if(typeof(file.mimetype) != 'undefined'){
29525             formData.append('mimetype', file.mimetype);
29526         }
29527         
29528         Roo.log(formData);
29529         
29530         if(this.fireEvent('prepare', this, formData) != false){
29531             this.xhr.send(formData);
29532         };
29533     }
29534 });
29535
29536 /*
29537 * Licence: LGPL
29538 */
29539
29540 /**
29541  * @class Roo.bootstrap.DocumentViewer
29542  * @extends Roo.bootstrap.Component
29543  * Bootstrap DocumentViewer class
29544  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29545  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29546  * 
29547  * @constructor
29548  * Create a new DocumentViewer
29549  * @param {Object} config The config object
29550  */
29551
29552 Roo.bootstrap.DocumentViewer = function(config){
29553     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29554     
29555     this.addEvents({
29556         /**
29557          * @event initial
29558          * Fire after initEvent
29559          * @param {Roo.bootstrap.DocumentViewer} this
29560          */
29561         "initial" : true,
29562         /**
29563          * @event click
29564          * Fire after click
29565          * @param {Roo.bootstrap.DocumentViewer} this
29566          */
29567         "click" : true,
29568         /**
29569          * @event download
29570          * Fire after download button
29571          * @param {Roo.bootstrap.DocumentViewer} this
29572          */
29573         "download" : true,
29574         /**
29575          * @event trash
29576          * Fire after trash button
29577          * @param {Roo.bootstrap.DocumentViewer} this
29578          */
29579         "trash" : true
29580         
29581     });
29582 };
29583
29584 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29585     
29586     showDownload : true,
29587     
29588     showTrash : true,
29589     
29590     getAutoCreate : function()
29591     {
29592         var cfg = {
29593             tag : 'div',
29594             cls : 'roo-document-viewer',
29595             cn : [
29596                 {
29597                     tag : 'div',
29598                     cls : 'roo-document-viewer-body',
29599                     cn : [
29600                         {
29601                             tag : 'div',
29602                             cls : 'roo-document-viewer-thumb',
29603                             cn : [
29604                                 {
29605                                     tag : 'img',
29606                                     cls : 'roo-document-viewer-image'
29607                                 }
29608                             ]
29609                         }
29610                     ]
29611                 },
29612                 {
29613                     tag : 'div',
29614                     cls : 'roo-document-viewer-footer',
29615                     cn : {
29616                         tag : 'div',
29617                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29618                         cn : [
29619                             {
29620                                 tag : 'div',
29621                                 cls : 'btn-group roo-document-viewer-download',
29622                                 cn : [
29623                                     {
29624                                         tag : 'button',
29625                                         cls : 'btn btn-default',
29626                                         html : '<i class="fa fa-download"></i>'
29627                                     }
29628                                 ]
29629                             },
29630                             {
29631                                 tag : 'div',
29632                                 cls : 'btn-group roo-document-viewer-trash',
29633                                 cn : [
29634                                     {
29635                                         tag : 'button',
29636                                         cls : 'btn btn-default',
29637                                         html : '<i class="fa fa-trash"></i>'
29638                                     }
29639                                 ]
29640                             }
29641                         ]
29642                     }
29643                 }
29644             ]
29645         };
29646         
29647         return cfg;
29648     },
29649     
29650     initEvents : function()
29651     {
29652         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29653         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29654         
29655         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29656         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29657         
29658         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29659         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29660         
29661         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29662         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29663         
29664         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29665         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29666         
29667         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29668         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29669         
29670         this.bodyEl.on('click', this.onClick, this);
29671         this.downloadBtn.on('click', this.onDownload, this);
29672         this.trashBtn.on('click', this.onTrash, this);
29673         
29674         this.downloadBtn.hide();
29675         this.trashBtn.hide();
29676         
29677         if(this.showDownload){
29678             this.downloadBtn.show();
29679         }
29680         
29681         if(this.showTrash){
29682             this.trashBtn.show();
29683         }
29684         
29685         if(!this.showDownload && !this.showTrash) {
29686             this.footerEl.hide();
29687         }
29688         
29689     },
29690     
29691     initial : function()
29692     {
29693         this.fireEvent('initial', this);
29694         
29695     },
29696     
29697     onClick : function(e)
29698     {
29699         e.preventDefault();
29700         
29701         this.fireEvent('click', this);
29702     },
29703     
29704     onDownload : function(e)
29705     {
29706         e.preventDefault();
29707         
29708         this.fireEvent('download', this);
29709     },
29710     
29711     onTrash : function(e)
29712     {
29713         e.preventDefault();
29714         
29715         this.fireEvent('trash', this);
29716     }
29717     
29718 });
29719 /*
29720  * - LGPL
29721  *
29722  * nav progress bar
29723  * 
29724  */
29725
29726 /**
29727  * @class Roo.bootstrap.NavProgressBar
29728  * @extends Roo.bootstrap.Component
29729  * Bootstrap NavProgressBar class
29730  * 
29731  * @constructor
29732  * Create a new nav progress bar
29733  * @param {Object} config The config object
29734  */
29735
29736 Roo.bootstrap.NavProgressBar = function(config){
29737     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29738
29739     this.bullets = this.bullets || [];
29740    
29741 //    Roo.bootstrap.NavProgressBar.register(this);
29742      this.addEvents({
29743         /**
29744              * @event changed
29745              * Fires when the active item changes
29746              * @param {Roo.bootstrap.NavProgressBar} this
29747              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29748              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29749          */
29750         'changed': true
29751      });
29752     
29753 };
29754
29755 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29756     
29757     bullets : [],
29758     barItems : [],
29759     
29760     getAutoCreate : function()
29761     {
29762         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29763         
29764         cfg = {
29765             tag : 'div',
29766             cls : 'roo-navigation-bar-group',
29767             cn : [
29768                 {
29769                     tag : 'div',
29770                     cls : 'roo-navigation-top-bar'
29771                 },
29772                 {
29773                     tag : 'div',
29774                     cls : 'roo-navigation-bullets-bar',
29775                     cn : [
29776                         {
29777                             tag : 'ul',
29778                             cls : 'roo-navigation-bar'
29779                         }
29780                     ]
29781                 },
29782                 
29783                 {
29784                     tag : 'div',
29785                     cls : 'roo-navigation-bottom-bar'
29786                 }
29787             ]
29788             
29789         };
29790         
29791         return cfg;
29792         
29793     },
29794     
29795     initEvents: function() 
29796     {
29797         
29798     },
29799     
29800     onRender : function(ct, position) 
29801     {
29802         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29803         
29804         if(this.bullets.length){
29805             Roo.each(this.bullets, function(b){
29806                this.addItem(b);
29807             }, this);
29808         }
29809         
29810         this.format();
29811         
29812     },
29813     
29814     addItem : function(cfg)
29815     {
29816         var item = new Roo.bootstrap.NavProgressItem(cfg);
29817         
29818         item.parentId = this.id;
29819         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29820         
29821         if(cfg.html){
29822             var top = new Roo.bootstrap.Element({
29823                 tag : 'div',
29824                 cls : 'roo-navigation-bar-text'
29825             });
29826             
29827             var bottom = new Roo.bootstrap.Element({
29828                 tag : 'div',
29829                 cls : 'roo-navigation-bar-text'
29830             });
29831             
29832             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29833             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29834             
29835             var topText = new Roo.bootstrap.Element({
29836                 tag : 'span',
29837                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29838             });
29839             
29840             var bottomText = new Roo.bootstrap.Element({
29841                 tag : 'span',
29842                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29843             });
29844             
29845             topText.onRender(top.el, null);
29846             bottomText.onRender(bottom.el, null);
29847             
29848             item.topEl = top;
29849             item.bottomEl = bottom;
29850         }
29851         
29852         this.barItems.push(item);
29853         
29854         return item;
29855     },
29856     
29857     getActive : function()
29858     {
29859         var active = false;
29860         
29861         Roo.each(this.barItems, function(v){
29862             
29863             if (!v.isActive()) {
29864                 return;
29865             }
29866             
29867             active = v;
29868             return false;
29869             
29870         });
29871         
29872         return active;
29873     },
29874     
29875     setActiveItem : function(item)
29876     {
29877         var prev = false;
29878         
29879         Roo.each(this.barItems, function(v){
29880             if (v.rid == item.rid) {
29881                 return ;
29882             }
29883             
29884             if (v.isActive()) {
29885                 v.setActive(false);
29886                 prev = v;
29887             }
29888         });
29889
29890         item.setActive(true);
29891         
29892         this.fireEvent('changed', this, item, prev);
29893     },
29894     
29895     getBarItem: function(rid)
29896     {
29897         var ret = false;
29898         
29899         Roo.each(this.barItems, function(e) {
29900             if (e.rid != rid) {
29901                 return;
29902             }
29903             
29904             ret =  e;
29905             return false;
29906         });
29907         
29908         return ret;
29909     },
29910     
29911     indexOfItem : function(item)
29912     {
29913         var index = false;
29914         
29915         Roo.each(this.barItems, function(v, i){
29916             
29917             if (v.rid != item.rid) {
29918                 return;
29919             }
29920             
29921             index = i;
29922             return false
29923         });
29924         
29925         return index;
29926     },
29927     
29928     setActiveNext : function()
29929     {
29930         var i = this.indexOfItem(this.getActive());
29931         
29932         if (i > this.barItems.length) {
29933             return;
29934         }
29935         
29936         this.setActiveItem(this.barItems[i+1]);
29937     },
29938     
29939     setActivePrev : function()
29940     {
29941         var i = this.indexOfItem(this.getActive());
29942         
29943         if (i  < 1) {
29944             return;
29945         }
29946         
29947         this.setActiveItem(this.barItems[i-1]);
29948     },
29949     
29950     format : function()
29951     {
29952         if(!this.barItems.length){
29953             return;
29954         }
29955      
29956         var width = 100 / this.barItems.length;
29957         
29958         Roo.each(this.barItems, function(i){
29959             i.el.setStyle('width', width + '%');
29960             i.topEl.el.setStyle('width', width + '%');
29961             i.bottomEl.el.setStyle('width', width + '%');
29962         }, this);
29963         
29964     }
29965     
29966 });
29967 /*
29968  * - LGPL
29969  *
29970  * Nav Progress Item
29971  * 
29972  */
29973
29974 /**
29975  * @class Roo.bootstrap.NavProgressItem
29976  * @extends Roo.bootstrap.Component
29977  * Bootstrap NavProgressItem class
29978  * @cfg {String} rid the reference id
29979  * @cfg {Boolean} active (true|false) Is item active default false
29980  * @cfg {Boolean} disabled (true|false) Is item active default false
29981  * @cfg {String} html
29982  * @cfg {String} position (top|bottom) text position default bottom
29983  * @cfg {String} icon show icon instead of number
29984  * 
29985  * @constructor
29986  * Create a new NavProgressItem
29987  * @param {Object} config The config object
29988  */
29989 Roo.bootstrap.NavProgressItem = function(config){
29990     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29991     this.addEvents({
29992         // raw events
29993         /**
29994          * @event click
29995          * The raw click event for the entire grid.
29996          * @param {Roo.bootstrap.NavProgressItem} this
29997          * @param {Roo.EventObject} e
29998          */
29999         "click" : true
30000     });
30001    
30002 };
30003
30004 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30005     
30006     rid : '',
30007     active : false,
30008     disabled : false,
30009     html : '',
30010     position : 'bottom',
30011     icon : false,
30012     
30013     getAutoCreate : function()
30014     {
30015         var iconCls = 'roo-navigation-bar-item-icon';
30016         
30017         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30018         
30019         var cfg = {
30020             tag: 'li',
30021             cls: 'roo-navigation-bar-item',
30022             cn : [
30023                 {
30024                     tag : 'i',
30025                     cls : iconCls
30026                 }
30027             ]
30028         };
30029         
30030         if(this.active){
30031             cfg.cls += ' active';
30032         }
30033         if(this.disabled){
30034             cfg.cls += ' disabled';
30035         }
30036         
30037         return cfg;
30038     },
30039     
30040     disable : function()
30041     {
30042         this.setDisabled(true);
30043     },
30044     
30045     enable : function()
30046     {
30047         this.setDisabled(false);
30048     },
30049     
30050     initEvents: function() 
30051     {
30052         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30053         
30054         this.iconEl.on('click', this.onClick, this);
30055     },
30056     
30057     onClick : function(e)
30058     {
30059         e.preventDefault();
30060         
30061         if(this.disabled){
30062             return;
30063         }
30064         
30065         if(this.fireEvent('click', this, e) === false){
30066             return;
30067         };
30068         
30069         this.parent().setActiveItem(this);
30070     },
30071     
30072     isActive: function () 
30073     {
30074         return this.active;
30075     },
30076     
30077     setActive : function(state)
30078     {
30079         if(this.active == state){
30080             return;
30081         }
30082         
30083         this.active = state;
30084         
30085         if (state) {
30086             this.el.addClass('active');
30087             return;
30088         }
30089         
30090         this.el.removeClass('active');
30091         
30092         return;
30093     },
30094     
30095     setDisabled : function(state)
30096     {
30097         if(this.disabled == state){
30098             return;
30099         }
30100         
30101         this.disabled = state;
30102         
30103         if (state) {
30104             this.el.addClass('disabled');
30105             return;
30106         }
30107         
30108         this.el.removeClass('disabled');
30109     },
30110     
30111     tooltipEl : function()
30112     {
30113         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30114     }
30115 });
30116  
30117
30118  /*
30119  * - LGPL
30120  *
30121  * FieldLabel
30122  * 
30123  */
30124
30125 /**
30126  * @class Roo.bootstrap.FieldLabel
30127  * @extends Roo.bootstrap.Component
30128  * Bootstrap FieldLabel class
30129  * @cfg {String} html contents of the element
30130  * @cfg {String} tag tag of the element default label
30131  * @cfg {String} cls class of the element
30132  * @cfg {String} target label target 
30133  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30134  * @cfg {String} invalidClass default "text-warning"
30135  * @cfg {String} validClass default "text-success"
30136  * @cfg {String} iconTooltip default "This field is required"
30137  * @cfg {String} indicatorpos (left|right) default left
30138  * 
30139  * @constructor
30140  * Create a new FieldLabel
30141  * @param {Object} config The config object
30142  */
30143
30144 Roo.bootstrap.FieldLabel = function(config){
30145     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30146     
30147     this.addEvents({
30148             /**
30149              * @event invalid
30150              * Fires after the field has been marked as invalid.
30151              * @param {Roo.form.FieldLabel} this
30152              * @param {String} msg The validation message
30153              */
30154             invalid : true,
30155             /**
30156              * @event valid
30157              * Fires after the field has been validated with no errors.
30158              * @param {Roo.form.FieldLabel} this
30159              */
30160             valid : true
30161         });
30162 };
30163
30164 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30165     
30166     tag: 'label',
30167     cls: '',
30168     html: '',
30169     target: '',
30170     allowBlank : true,
30171     invalidClass : 'has-warning',
30172     validClass : 'has-success',
30173     iconTooltip : 'This field is required',
30174     indicatorpos : 'left',
30175     
30176     getAutoCreate : function(){
30177         
30178         var cls = "";
30179         if (!this.allowBlank) {
30180             cls  = "visible";
30181         }
30182         
30183         var cfg = {
30184             tag : this.tag,
30185             cls : 'roo-bootstrap-field-label ' + this.cls,
30186             for : this.target,
30187             cn : [
30188                 {
30189                     tag : 'i',
30190                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30191                     tooltip : this.iconTooltip
30192                 },
30193                 {
30194                     tag : 'span',
30195                     html : this.html
30196                 }
30197             ] 
30198         };
30199         
30200         if(this.indicatorpos == 'right'){
30201             var cfg = {
30202                 tag : this.tag,
30203                 cls : 'roo-bootstrap-field-label ' + this.cls,
30204                 for : this.target,
30205                 cn : [
30206                     {
30207                         tag : 'span',
30208                         html : this.html
30209                     },
30210                     {
30211                         tag : 'i',
30212                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30213                         tooltip : this.iconTooltip
30214                     }
30215                 ] 
30216             };
30217         }
30218         
30219         return cfg;
30220     },
30221     
30222     initEvents: function() 
30223     {
30224         Roo.bootstrap.Element.superclass.initEvents.call(this);
30225         
30226         this.indicator = this.indicatorEl();
30227         
30228         if(this.indicator){
30229             this.indicator.removeClass('visible');
30230             this.indicator.addClass('invisible');
30231         }
30232         
30233         Roo.bootstrap.FieldLabel.register(this);
30234     },
30235     
30236     indicatorEl : function()
30237     {
30238         var indicator = this.el.select('i.roo-required-indicator',true).first();
30239         
30240         if(!indicator){
30241             return false;
30242         }
30243         
30244         return indicator;
30245         
30246     },
30247     
30248     /**
30249      * Mark this field as valid
30250      */
30251     markValid : function()
30252     {
30253         if(this.indicator){
30254             this.indicator.removeClass('visible');
30255             this.indicator.addClass('invisible');
30256         }
30257         
30258         this.el.removeClass(this.invalidClass);
30259         
30260         this.el.addClass(this.validClass);
30261         
30262         this.fireEvent('valid', this);
30263     },
30264     
30265     /**
30266      * Mark this field as invalid
30267      * @param {String} msg The validation message
30268      */
30269     markInvalid : function(msg)
30270     {
30271         if(this.indicator){
30272             this.indicator.removeClass('invisible');
30273             this.indicator.addClass('visible');
30274         }
30275         
30276         this.el.removeClass(this.validClass);
30277         
30278         this.el.addClass(this.invalidClass);
30279         
30280         this.fireEvent('invalid', this, msg);
30281     }
30282     
30283    
30284 });
30285
30286 Roo.apply(Roo.bootstrap.FieldLabel, {
30287     
30288     groups: {},
30289     
30290      /**
30291     * register a FieldLabel Group
30292     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30293     */
30294     register : function(label)
30295     {
30296         if(this.groups.hasOwnProperty(label.target)){
30297             return;
30298         }
30299      
30300         this.groups[label.target] = label;
30301         
30302     },
30303     /**
30304     * fetch a FieldLabel Group based on the target
30305     * @param {string} target
30306     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30307     */
30308     get: function(target) {
30309         if (typeof(this.groups[target]) == 'undefined') {
30310             return false;
30311         }
30312         
30313         return this.groups[target] ;
30314     }
30315 });
30316
30317  
30318
30319  /*
30320  * - LGPL
30321  *
30322  * page DateSplitField.
30323  * 
30324  */
30325
30326
30327 /**
30328  * @class Roo.bootstrap.DateSplitField
30329  * @extends Roo.bootstrap.Component
30330  * Bootstrap DateSplitField class
30331  * @cfg {string} fieldLabel - the label associated
30332  * @cfg {Number} labelWidth set the width of label (0-12)
30333  * @cfg {String} labelAlign (top|left)
30334  * @cfg {Boolean} dayAllowBlank (true|false) default false
30335  * @cfg {Boolean} monthAllowBlank (true|false) default false
30336  * @cfg {Boolean} yearAllowBlank (true|false) default false
30337  * @cfg {string} dayPlaceholder 
30338  * @cfg {string} monthPlaceholder
30339  * @cfg {string} yearPlaceholder
30340  * @cfg {string} dayFormat default 'd'
30341  * @cfg {string} monthFormat default 'm'
30342  * @cfg {string} yearFormat default 'Y'
30343  * @cfg {Number} labellg set the width of label (1-12)
30344  * @cfg {Number} labelmd set the width of label (1-12)
30345  * @cfg {Number} labelsm set the width of label (1-12)
30346  * @cfg {Number} labelxs set the width of label (1-12)
30347
30348  *     
30349  * @constructor
30350  * Create a new DateSplitField
30351  * @param {Object} config The config object
30352  */
30353
30354 Roo.bootstrap.DateSplitField = function(config){
30355     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30356     
30357     this.addEvents({
30358         // raw events
30359          /**
30360          * @event years
30361          * getting the data of years
30362          * @param {Roo.bootstrap.DateSplitField} this
30363          * @param {Object} years
30364          */
30365         "years" : true,
30366         /**
30367          * @event days
30368          * getting the data of days
30369          * @param {Roo.bootstrap.DateSplitField} this
30370          * @param {Object} days
30371          */
30372         "days" : true,
30373         /**
30374          * @event invalid
30375          * Fires after the field has been marked as invalid.
30376          * @param {Roo.form.Field} this
30377          * @param {String} msg The validation message
30378          */
30379         invalid : true,
30380        /**
30381          * @event valid
30382          * Fires after the field has been validated with no errors.
30383          * @param {Roo.form.Field} this
30384          */
30385         valid : true
30386     });
30387 };
30388
30389 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30390     
30391     fieldLabel : '',
30392     labelAlign : 'top',
30393     labelWidth : 3,
30394     dayAllowBlank : false,
30395     monthAllowBlank : false,
30396     yearAllowBlank : false,
30397     dayPlaceholder : '',
30398     monthPlaceholder : '',
30399     yearPlaceholder : '',
30400     dayFormat : 'd',
30401     monthFormat : 'm',
30402     yearFormat : 'Y',
30403     isFormField : true,
30404     labellg : 0,
30405     labelmd : 0,
30406     labelsm : 0,
30407     labelxs : 0,
30408     
30409     getAutoCreate : function()
30410     {
30411         var cfg = {
30412             tag : 'div',
30413             cls : 'row roo-date-split-field-group',
30414             cn : [
30415                 {
30416                     tag : 'input',
30417                     type : 'hidden',
30418                     cls : 'form-hidden-field roo-date-split-field-group-value',
30419                     name : this.name
30420                 }
30421             ]
30422         };
30423         
30424         var labelCls = 'col-md-12';
30425         var contentCls = 'col-md-4';
30426         
30427         if(this.fieldLabel){
30428             
30429             var label = {
30430                 tag : 'div',
30431                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30432                 cn : [
30433                     {
30434                         tag : 'label',
30435                         html : this.fieldLabel
30436                     }
30437                 ]
30438             };
30439             
30440             if(this.labelAlign == 'left'){
30441             
30442                 if(this.labelWidth > 12){
30443                     label.style = "width: " + this.labelWidth + 'px';
30444                 }
30445
30446                 if(this.labelWidth < 13 && this.labelmd == 0){
30447                     this.labelmd = this.labelWidth;
30448                 }
30449
30450                 if(this.labellg > 0){
30451                     labelCls = ' col-lg-' + this.labellg;
30452                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30453                 }
30454
30455                 if(this.labelmd > 0){
30456                     labelCls = ' col-md-' + this.labelmd;
30457                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30458                 }
30459
30460                 if(this.labelsm > 0){
30461                     labelCls = ' col-sm-' + this.labelsm;
30462                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30463                 }
30464
30465                 if(this.labelxs > 0){
30466                     labelCls = ' col-xs-' + this.labelxs;
30467                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30468                 }
30469             }
30470             
30471             label.cls += ' ' + labelCls;
30472             
30473             cfg.cn.push(label);
30474         }
30475         
30476         Roo.each(['day', 'month', 'year'], function(t){
30477             cfg.cn.push({
30478                 tag : 'div',
30479                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30480             });
30481         }, this);
30482         
30483         return cfg;
30484     },
30485     
30486     inputEl: function ()
30487     {
30488         return this.el.select('.roo-date-split-field-group-value', true).first();
30489     },
30490     
30491     onRender : function(ct, position) 
30492     {
30493         var _this = this;
30494         
30495         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30496         
30497         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30498         
30499         this.dayField = new Roo.bootstrap.ComboBox({
30500             allowBlank : this.dayAllowBlank,
30501             alwaysQuery : true,
30502             displayField : 'value',
30503             editable : false,
30504             fieldLabel : '',
30505             forceSelection : true,
30506             mode : 'local',
30507             placeholder : this.dayPlaceholder,
30508             selectOnFocus : true,
30509             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30510             triggerAction : 'all',
30511             typeAhead : true,
30512             valueField : 'value',
30513             store : new Roo.data.SimpleStore({
30514                 data : (function() {    
30515                     var days = [];
30516                     _this.fireEvent('days', _this, days);
30517                     return days;
30518                 })(),
30519                 fields : [ 'value' ]
30520             }),
30521             listeners : {
30522                 select : function (_self, record, index)
30523                 {
30524                     _this.setValue(_this.getValue());
30525                 }
30526             }
30527         });
30528
30529         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30530         
30531         this.monthField = new Roo.bootstrap.MonthField({
30532             after : '<i class=\"fa fa-calendar\"></i>',
30533             allowBlank : this.monthAllowBlank,
30534             placeholder : this.monthPlaceholder,
30535             readOnly : true,
30536             listeners : {
30537                 render : function (_self)
30538                 {
30539                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30540                         e.preventDefault();
30541                         _self.focus();
30542                     });
30543                 },
30544                 select : function (_self, oldvalue, newvalue)
30545                 {
30546                     _this.setValue(_this.getValue());
30547                 }
30548             }
30549         });
30550         
30551         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30552         
30553         this.yearField = new Roo.bootstrap.ComboBox({
30554             allowBlank : this.yearAllowBlank,
30555             alwaysQuery : true,
30556             displayField : 'value',
30557             editable : false,
30558             fieldLabel : '',
30559             forceSelection : true,
30560             mode : 'local',
30561             placeholder : this.yearPlaceholder,
30562             selectOnFocus : true,
30563             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30564             triggerAction : 'all',
30565             typeAhead : true,
30566             valueField : 'value',
30567             store : new Roo.data.SimpleStore({
30568                 data : (function() {
30569                     var years = [];
30570                     _this.fireEvent('years', _this, years);
30571                     return years;
30572                 })(),
30573                 fields : [ 'value' ]
30574             }),
30575             listeners : {
30576                 select : function (_self, record, index)
30577                 {
30578                     _this.setValue(_this.getValue());
30579                 }
30580             }
30581         });
30582
30583         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30584     },
30585     
30586     setValue : function(v, format)
30587     {
30588         this.inputEl.dom.value = v;
30589         
30590         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30591         
30592         var d = Date.parseDate(v, f);
30593         
30594         if(!d){
30595             this.validate();
30596             return;
30597         }
30598         
30599         this.setDay(d.format(this.dayFormat));
30600         this.setMonth(d.format(this.monthFormat));
30601         this.setYear(d.format(this.yearFormat));
30602         
30603         this.validate();
30604         
30605         return;
30606     },
30607     
30608     setDay : function(v)
30609     {
30610         this.dayField.setValue(v);
30611         this.inputEl.dom.value = this.getValue();
30612         this.validate();
30613         return;
30614     },
30615     
30616     setMonth : function(v)
30617     {
30618         this.monthField.setValue(v, true);
30619         this.inputEl.dom.value = this.getValue();
30620         this.validate();
30621         return;
30622     },
30623     
30624     setYear : function(v)
30625     {
30626         this.yearField.setValue(v);
30627         this.inputEl.dom.value = this.getValue();
30628         this.validate();
30629         return;
30630     },
30631     
30632     getDay : function()
30633     {
30634         return this.dayField.getValue();
30635     },
30636     
30637     getMonth : function()
30638     {
30639         return this.monthField.getValue();
30640     },
30641     
30642     getYear : function()
30643     {
30644         return this.yearField.getValue();
30645     },
30646     
30647     getValue : function()
30648     {
30649         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30650         
30651         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30652         
30653         return date;
30654     },
30655     
30656     reset : function()
30657     {
30658         this.setDay('');
30659         this.setMonth('');
30660         this.setYear('');
30661         this.inputEl.dom.value = '';
30662         this.validate();
30663         return;
30664     },
30665     
30666     validate : function()
30667     {
30668         var d = this.dayField.validate();
30669         var m = this.monthField.validate();
30670         var y = this.yearField.validate();
30671         
30672         var valid = true;
30673         
30674         if(
30675                 (!this.dayAllowBlank && !d) ||
30676                 (!this.monthAllowBlank && !m) ||
30677                 (!this.yearAllowBlank && !y)
30678         ){
30679             valid = false;
30680         }
30681         
30682         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30683             return valid;
30684         }
30685         
30686         if(valid){
30687             this.markValid();
30688             return valid;
30689         }
30690         
30691         this.markInvalid();
30692         
30693         return valid;
30694     },
30695     
30696     markValid : function()
30697     {
30698         
30699         var label = this.el.select('label', true).first();
30700         var icon = this.el.select('i.fa-star', true).first();
30701
30702         if(label && icon){
30703             icon.remove();
30704         }
30705         
30706         this.fireEvent('valid', this);
30707     },
30708     
30709      /**
30710      * Mark this field as invalid
30711      * @param {String} msg The validation message
30712      */
30713     markInvalid : function(msg)
30714     {
30715         
30716         var label = this.el.select('label', true).first();
30717         var icon = this.el.select('i.fa-star', true).first();
30718
30719         if(label && !icon){
30720             this.el.select('.roo-date-split-field-label', true).createChild({
30721                 tag : 'i',
30722                 cls : 'text-danger fa fa-lg fa-star',
30723                 tooltip : 'This field is required',
30724                 style : 'margin-right:5px;'
30725             }, label, true);
30726         }
30727         
30728         this.fireEvent('invalid', this, msg);
30729     },
30730     
30731     clearInvalid : function()
30732     {
30733         var label = this.el.select('label', true).first();
30734         var icon = this.el.select('i.fa-star', true).first();
30735
30736         if(label && icon){
30737             icon.remove();
30738         }
30739         
30740         this.fireEvent('valid', this);
30741     },
30742     
30743     getName: function()
30744     {
30745         return this.name;
30746     }
30747     
30748 });
30749
30750  /**
30751  *
30752  * This is based on 
30753  * http://masonry.desandro.com
30754  *
30755  * The idea is to render all the bricks based on vertical width...
30756  *
30757  * The original code extends 'outlayer' - we might need to use that....
30758  * 
30759  */
30760
30761
30762 /**
30763  * @class Roo.bootstrap.LayoutMasonry
30764  * @extends Roo.bootstrap.Component
30765  * Bootstrap Layout Masonry class
30766  * 
30767  * @constructor
30768  * Create a new Element
30769  * @param {Object} config The config object
30770  */
30771
30772 Roo.bootstrap.LayoutMasonry = function(config){
30773     
30774     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30775     
30776     this.bricks = [];
30777     
30778     Roo.bootstrap.LayoutMasonry.register(this);
30779     
30780     this.addEvents({
30781         // raw events
30782         /**
30783          * @event layout
30784          * Fire after layout the items
30785          * @param {Roo.bootstrap.LayoutMasonry} this
30786          * @param {Roo.EventObject} e
30787          */
30788         "layout" : true
30789     });
30790     
30791 };
30792
30793 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30794     
30795     /**
30796      * @cfg {Boolean} isLayoutInstant = no animation?
30797      */   
30798     isLayoutInstant : false, // needed?
30799    
30800     /**
30801      * @cfg {Number} boxWidth  width of the columns
30802      */   
30803     boxWidth : 450,
30804     
30805       /**
30806      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30807      */   
30808     boxHeight : 0,
30809     
30810     /**
30811      * @cfg {Number} padWidth padding below box..
30812      */   
30813     padWidth : 10, 
30814     
30815     /**
30816      * @cfg {Number} gutter gutter width..
30817      */   
30818     gutter : 10,
30819     
30820      /**
30821      * @cfg {Number} maxCols maximum number of columns
30822      */   
30823     
30824     maxCols: 0,
30825     
30826     /**
30827      * @cfg {Boolean} isAutoInitial defalut true
30828      */   
30829     isAutoInitial : true, 
30830     
30831     containerWidth: 0,
30832     
30833     /**
30834      * @cfg {Boolean} isHorizontal defalut false
30835      */   
30836     isHorizontal : false, 
30837
30838     currentSize : null,
30839     
30840     tag: 'div',
30841     
30842     cls: '',
30843     
30844     bricks: null, //CompositeElement
30845     
30846     cols : 1,
30847     
30848     _isLayoutInited : false,
30849     
30850 //    isAlternative : false, // only use for vertical layout...
30851     
30852     /**
30853      * @cfg {Number} alternativePadWidth padding below box..
30854      */   
30855     alternativePadWidth : 50,
30856     
30857     selectedBrick : [],
30858     
30859     getAutoCreate : function(){
30860         
30861         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30862         
30863         var cfg = {
30864             tag: this.tag,
30865             cls: 'blog-masonary-wrapper ' + this.cls,
30866             cn : {
30867                 cls : 'mas-boxes masonary'
30868             }
30869         };
30870         
30871         return cfg;
30872     },
30873     
30874     getChildContainer: function( )
30875     {
30876         if (this.boxesEl) {
30877             return this.boxesEl;
30878         }
30879         
30880         this.boxesEl = this.el.select('.mas-boxes').first();
30881         
30882         return this.boxesEl;
30883     },
30884     
30885     
30886     initEvents : function()
30887     {
30888         var _this = this;
30889         
30890         if(this.isAutoInitial){
30891             Roo.log('hook children rendered');
30892             this.on('childrenrendered', function() {
30893                 Roo.log('children rendered');
30894                 _this.initial();
30895             } ,this);
30896         }
30897     },
30898     
30899     initial : function()
30900     {
30901         this.selectedBrick = [];
30902         
30903         this.currentSize = this.el.getBox(true);
30904         
30905         Roo.EventManager.onWindowResize(this.resize, this); 
30906
30907         if(!this.isAutoInitial){
30908             this.layout();
30909             return;
30910         }
30911         
30912         this.layout();
30913         
30914         return;
30915         //this.layout.defer(500,this);
30916         
30917     },
30918     
30919     resize : function()
30920     {
30921         var cs = this.el.getBox(true);
30922         
30923         if (
30924                 this.currentSize.width == cs.width && 
30925                 this.currentSize.x == cs.x && 
30926                 this.currentSize.height == cs.height && 
30927                 this.currentSize.y == cs.y 
30928         ) {
30929             Roo.log("no change in with or X or Y");
30930             return;
30931         }
30932         
30933         this.currentSize = cs;
30934         
30935         this.layout();
30936         
30937     },
30938     
30939     layout : function()
30940     {   
30941         this._resetLayout();
30942         
30943         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30944         
30945         this.layoutItems( isInstant );
30946       
30947         this._isLayoutInited = true;
30948         
30949         this.fireEvent('layout', this);
30950         
30951     },
30952     
30953     _resetLayout : function()
30954     {
30955         if(this.isHorizontal){
30956             this.horizontalMeasureColumns();
30957             return;
30958         }
30959         
30960         this.verticalMeasureColumns();
30961         
30962     },
30963     
30964     verticalMeasureColumns : function()
30965     {
30966         this.getContainerWidth();
30967         
30968 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30969 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30970 //            return;
30971 //        }
30972         
30973         var boxWidth = this.boxWidth + this.padWidth;
30974         
30975         if(this.containerWidth < this.boxWidth){
30976             boxWidth = this.containerWidth
30977         }
30978         
30979         var containerWidth = this.containerWidth;
30980         
30981         var cols = Math.floor(containerWidth / boxWidth);
30982         
30983         this.cols = Math.max( cols, 1 );
30984         
30985         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30986         
30987         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30988         
30989         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30990         
30991         this.colWidth = boxWidth + avail - this.padWidth;
30992         
30993         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30994         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30995     },
30996     
30997     horizontalMeasureColumns : function()
30998     {
30999         this.getContainerWidth();
31000         
31001         var boxWidth = this.boxWidth;
31002         
31003         if(this.containerWidth < boxWidth){
31004             boxWidth = this.containerWidth;
31005         }
31006         
31007         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31008         
31009         this.el.setHeight(boxWidth);
31010         
31011     },
31012     
31013     getContainerWidth : function()
31014     {
31015         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31016     },
31017     
31018     layoutItems : function( isInstant )
31019     {
31020         Roo.log(this.bricks);
31021         
31022         var items = Roo.apply([], this.bricks);
31023         
31024         if(this.isHorizontal){
31025             this._horizontalLayoutItems( items , isInstant );
31026             return;
31027         }
31028         
31029 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31030 //            this._verticalAlternativeLayoutItems( items , isInstant );
31031 //            return;
31032 //        }
31033         
31034         this._verticalLayoutItems( items , isInstant );
31035         
31036     },
31037     
31038     _verticalLayoutItems : function ( items , isInstant)
31039     {
31040         if ( !items || !items.length ) {
31041             return;
31042         }
31043         
31044         var standard = [
31045             ['xs', 'xs', 'xs', 'tall'],
31046             ['xs', 'xs', 'tall'],
31047             ['xs', 'xs', 'sm'],
31048             ['xs', 'xs', 'xs'],
31049             ['xs', 'tall'],
31050             ['xs', 'sm'],
31051             ['xs', 'xs'],
31052             ['xs'],
31053             
31054             ['sm', 'xs', 'xs'],
31055             ['sm', 'xs'],
31056             ['sm'],
31057             
31058             ['tall', 'xs', 'xs', 'xs'],
31059             ['tall', 'xs', 'xs'],
31060             ['tall', 'xs'],
31061             ['tall']
31062             
31063         ];
31064         
31065         var queue = [];
31066         
31067         var boxes = [];
31068         
31069         var box = [];
31070         
31071         Roo.each(items, function(item, k){
31072             
31073             switch (item.size) {
31074                 // these layouts take up a full box,
31075                 case 'md' :
31076                 case 'md-left' :
31077                 case 'md-right' :
31078                 case 'wide' :
31079                     
31080                     if(box.length){
31081                         boxes.push(box);
31082                         box = [];
31083                     }
31084                     
31085                     boxes.push([item]);
31086                     
31087                     break;
31088                     
31089                 case 'xs' :
31090                 case 'sm' :
31091                 case 'tall' :
31092                     
31093                     box.push(item);
31094                     
31095                     break;
31096                 default :
31097                     break;
31098                     
31099             }
31100             
31101         }, this);
31102         
31103         if(box.length){
31104             boxes.push(box);
31105             box = [];
31106         }
31107         
31108         var filterPattern = function(box, length)
31109         {
31110             if(!box.length){
31111                 return;
31112             }
31113             
31114             var match = false;
31115             
31116             var pattern = box.slice(0, length);
31117             
31118             var format = [];
31119             
31120             Roo.each(pattern, function(i){
31121                 format.push(i.size);
31122             }, this);
31123             
31124             Roo.each(standard, function(s){
31125                 
31126                 if(String(s) != String(format)){
31127                     return;
31128                 }
31129                 
31130                 match = true;
31131                 return false;
31132                 
31133             }, this);
31134             
31135             if(!match && length == 1){
31136                 return;
31137             }
31138             
31139             if(!match){
31140                 filterPattern(box, length - 1);
31141                 return;
31142             }
31143                 
31144             queue.push(pattern);
31145
31146             box = box.slice(length, box.length);
31147
31148             filterPattern(box, 4);
31149
31150             return;
31151             
31152         }
31153         
31154         Roo.each(boxes, function(box, k){
31155             
31156             if(!box.length){
31157                 return;
31158             }
31159             
31160             if(box.length == 1){
31161                 queue.push(box);
31162                 return;
31163             }
31164             
31165             filterPattern(box, 4);
31166             
31167         }, this);
31168         
31169         this._processVerticalLayoutQueue( queue, isInstant );
31170         
31171     },
31172     
31173 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31174 //    {
31175 //        if ( !items || !items.length ) {
31176 //            return;
31177 //        }
31178 //
31179 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31180 //        
31181 //    },
31182     
31183     _horizontalLayoutItems : function ( items , isInstant)
31184     {
31185         if ( !items || !items.length || items.length < 3) {
31186             return;
31187         }
31188         
31189         items.reverse();
31190         
31191         var eItems = items.slice(0, 3);
31192         
31193         items = items.slice(3, items.length);
31194         
31195         var standard = [
31196             ['xs', 'xs', 'xs', 'wide'],
31197             ['xs', 'xs', 'wide'],
31198             ['xs', 'xs', 'sm'],
31199             ['xs', 'xs', 'xs'],
31200             ['xs', 'wide'],
31201             ['xs', 'sm'],
31202             ['xs', 'xs'],
31203             ['xs'],
31204             
31205             ['sm', 'xs', 'xs'],
31206             ['sm', 'xs'],
31207             ['sm'],
31208             
31209             ['wide', 'xs', 'xs', 'xs'],
31210             ['wide', 'xs', 'xs'],
31211             ['wide', 'xs'],
31212             ['wide'],
31213             
31214             ['wide-thin']
31215         ];
31216         
31217         var queue = [];
31218         
31219         var boxes = [];
31220         
31221         var box = [];
31222         
31223         Roo.each(items, function(item, k){
31224             
31225             switch (item.size) {
31226                 case 'md' :
31227                 case 'md-left' :
31228                 case 'md-right' :
31229                 case 'tall' :
31230                     
31231                     if(box.length){
31232                         boxes.push(box);
31233                         box = [];
31234                     }
31235                     
31236                     boxes.push([item]);
31237                     
31238                     break;
31239                     
31240                 case 'xs' :
31241                 case 'sm' :
31242                 case 'wide' :
31243                 case 'wide-thin' :
31244                     
31245                     box.push(item);
31246                     
31247                     break;
31248                 default :
31249                     break;
31250                     
31251             }
31252             
31253         }, this);
31254         
31255         if(box.length){
31256             boxes.push(box);
31257             box = [];
31258         }
31259         
31260         var filterPattern = function(box, length)
31261         {
31262             if(!box.length){
31263                 return;
31264             }
31265             
31266             var match = false;
31267             
31268             var pattern = box.slice(0, length);
31269             
31270             var format = [];
31271             
31272             Roo.each(pattern, function(i){
31273                 format.push(i.size);
31274             }, this);
31275             
31276             Roo.each(standard, function(s){
31277                 
31278                 if(String(s) != String(format)){
31279                     return;
31280                 }
31281                 
31282                 match = true;
31283                 return false;
31284                 
31285             }, this);
31286             
31287             if(!match && length == 1){
31288                 return;
31289             }
31290             
31291             if(!match){
31292                 filterPattern(box, length - 1);
31293                 return;
31294             }
31295                 
31296             queue.push(pattern);
31297
31298             box = box.slice(length, box.length);
31299
31300             filterPattern(box, 4);
31301
31302             return;
31303             
31304         }
31305         
31306         Roo.each(boxes, function(box, k){
31307             
31308             if(!box.length){
31309                 return;
31310             }
31311             
31312             if(box.length == 1){
31313                 queue.push(box);
31314                 return;
31315             }
31316             
31317             filterPattern(box, 4);
31318             
31319         }, this);
31320         
31321         
31322         var prune = [];
31323         
31324         var pos = this.el.getBox(true);
31325         
31326         var minX = pos.x;
31327         
31328         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31329         
31330         var hit_end = false;
31331         
31332         Roo.each(queue, function(box){
31333             
31334             if(hit_end){
31335                 
31336                 Roo.each(box, function(b){
31337                 
31338                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31339                     b.el.hide();
31340
31341                 }, this);
31342
31343                 return;
31344             }
31345             
31346             var mx = 0;
31347             
31348             Roo.each(box, function(b){
31349                 
31350                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31351                 b.el.show();
31352
31353                 mx = Math.max(mx, b.x);
31354                 
31355             }, this);
31356             
31357             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31358             
31359             if(maxX < minX){
31360                 
31361                 Roo.each(box, function(b){
31362                 
31363                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31364                     b.el.hide();
31365                     
31366                 }, this);
31367                 
31368                 hit_end = true;
31369                 
31370                 return;
31371             }
31372             
31373             prune.push(box);
31374             
31375         }, this);
31376         
31377         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31378     },
31379     
31380     /** Sets position of item in DOM
31381     * @param {Element} item
31382     * @param {Number} x - horizontal position
31383     * @param {Number} y - vertical position
31384     * @param {Boolean} isInstant - disables transitions
31385     */
31386     _processVerticalLayoutQueue : function( queue, isInstant )
31387     {
31388         var pos = this.el.getBox(true);
31389         var x = pos.x;
31390         var y = pos.y;
31391         var maxY = [];
31392         
31393         for (var i = 0; i < this.cols; i++){
31394             maxY[i] = pos.y;
31395         }
31396         
31397         Roo.each(queue, function(box, k){
31398             
31399             var col = k % this.cols;
31400             
31401             Roo.each(box, function(b,kk){
31402                 
31403                 b.el.position('absolute');
31404                 
31405                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31406                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31407                 
31408                 if(b.size == 'md-left' || b.size == 'md-right'){
31409                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31410                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31411                 }
31412                 
31413                 b.el.setWidth(width);
31414                 b.el.setHeight(height);
31415                 // iframe?
31416                 b.el.select('iframe',true).setSize(width,height);
31417                 
31418             }, this);
31419             
31420             for (var i = 0; i < this.cols; i++){
31421                 
31422                 if(maxY[i] < maxY[col]){
31423                     col = i;
31424                     continue;
31425                 }
31426                 
31427                 col = Math.min(col, i);
31428                 
31429             }
31430             
31431             x = pos.x + col * (this.colWidth + this.padWidth);
31432             
31433             y = maxY[col];
31434             
31435             var positions = [];
31436             
31437             switch (box.length){
31438                 case 1 :
31439                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31440                     break;
31441                 case 2 :
31442                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31443                     break;
31444                 case 3 :
31445                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31446                     break;
31447                 case 4 :
31448                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31449                     break;
31450                 default :
31451                     break;
31452             }
31453             
31454             Roo.each(box, function(b,kk){
31455                 
31456                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31457                 
31458                 var sz = b.el.getSize();
31459                 
31460                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31461                 
31462             }, this);
31463             
31464         }, this);
31465         
31466         var mY = 0;
31467         
31468         for (var i = 0; i < this.cols; i++){
31469             mY = Math.max(mY, maxY[i]);
31470         }
31471         
31472         this.el.setHeight(mY - pos.y);
31473         
31474     },
31475     
31476 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31477 //    {
31478 //        var pos = this.el.getBox(true);
31479 //        var x = pos.x;
31480 //        var y = pos.y;
31481 //        var maxX = pos.right;
31482 //        
31483 //        var maxHeight = 0;
31484 //        
31485 //        Roo.each(items, function(item, k){
31486 //            
31487 //            var c = k % 2;
31488 //            
31489 //            item.el.position('absolute');
31490 //                
31491 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31492 //
31493 //            item.el.setWidth(width);
31494 //
31495 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31496 //
31497 //            item.el.setHeight(height);
31498 //            
31499 //            if(c == 0){
31500 //                item.el.setXY([x, y], isInstant ? false : true);
31501 //            } else {
31502 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31503 //            }
31504 //            
31505 //            y = y + height + this.alternativePadWidth;
31506 //            
31507 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31508 //            
31509 //        }, this);
31510 //        
31511 //        this.el.setHeight(maxHeight);
31512 //        
31513 //    },
31514     
31515     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31516     {
31517         var pos = this.el.getBox(true);
31518         
31519         var minX = pos.x;
31520         var minY = pos.y;
31521         
31522         var maxX = pos.right;
31523         
31524         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31525         
31526         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31527         
31528         Roo.each(queue, function(box, k){
31529             
31530             Roo.each(box, function(b, kk){
31531                 
31532                 b.el.position('absolute');
31533                 
31534                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31535                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31536                 
31537                 if(b.size == 'md-left' || b.size == 'md-right'){
31538                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31539                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31540                 }
31541                 
31542                 b.el.setWidth(width);
31543                 b.el.setHeight(height);
31544                 
31545             }, this);
31546             
31547             if(!box.length){
31548                 return;
31549             }
31550             
31551             var positions = [];
31552             
31553             switch (box.length){
31554                 case 1 :
31555                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31556                     break;
31557                 case 2 :
31558                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31559                     break;
31560                 case 3 :
31561                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31562                     break;
31563                 case 4 :
31564                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31565                     break;
31566                 default :
31567                     break;
31568             }
31569             
31570             Roo.each(box, function(b,kk){
31571                 
31572                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31573                 
31574                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31575                 
31576             }, this);
31577             
31578         }, this);
31579         
31580     },
31581     
31582     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31583     {
31584         Roo.each(eItems, function(b,k){
31585             
31586             b.size = (k == 0) ? 'sm' : 'xs';
31587             b.x = (k == 0) ? 2 : 1;
31588             b.y = (k == 0) ? 2 : 1;
31589             
31590             b.el.position('absolute');
31591             
31592             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31593                 
31594             b.el.setWidth(width);
31595             
31596             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31597             
31598             b.el.setHeight(height);
31599             
31600         }, this);
31601
31602         var positions = [];
31603         
31604         positions.push({
31605             x : maxX - this.unitWidth * 2 - this.gutter,
31606             y : minY
31607         });
31608         
31609         positions.push({
31610             x : maxX - this.unitWidth,
31611             y : minY + (this.unitWidth + this.gutter) * 2
31612         });
31613         
31614         positions.push({
31615             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31616             y : minY
31617         });
31618         
31619         Roo.each(eItems, function(b,k){
31620             
31621             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31622
31623         }, this);
31624         
31625     },
31626     
31627     getVerticalOneBoxColPositions : function(x, y, box)
31628     {
31629         var pos = [];
31630         
31631         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31632         
31633         if(box[0].size == 'md-left'){
31634             rand = 0;
31635         }
31636         
31637         if(box[0].size == 'md-right'){
31638             rand = 1;
31639         }
31640         
31641         pos.push({
31642             x : x + (this.unitWidth + this.gutter) * rand,
31643             y : y
31644         });
31645         
31646         return pos;
31647     },
31648     
31649     getVerticalTwoBoxColPositions : function(x, y, box)
31650     {
31651         var pos = [];
31652         
31653         if(box[0].size == 'xs'){
31654             
31655             pos.push({
31656                 x : x,
31657                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31658             });
31659
31660             pos.push({
31661                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31662                 y : y
31663             });
31664             
31665             return pos;
31666             
31667         }
31668         
31669         pos.push({
31670             x : x,
31671             y : y
31672         });
31673
31674         pos.push({
31675             x : x + (this.unitWidth + this.gutter) * 2,
31676             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31677         });
31678         
31679         return pos;
31680         
31681     },
31682     
31683     getVerticalThreeBoxColPositions : function(x, y, box)
31684     {
31685         var pos = [];
31686         
31687         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31688             
31689             pos.push({
31690                 x : x,
31691                 y : y
31692             });
31693
31694             pos.push({
31695                 x : x + (this.unitWidth + this.gutter) * 1,
31696                 y : y
31697             });
31698             
31699             pos.push({
31700                 x : x + (this.unitWidth + this.gutter) * 2,
31701                 y : y
31702             });
31703             
31704             return pos;
31705             
31706         }
31707         
31708         if(box[0].size == 'xs' && box[1].size == 'xs'){
31709             
31710             pos.push({
31711                 x : x,
31712                 y : y
31713             });
31714
31715             pos.push({
31716                 x : x,
31717                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31718             });
31719             
31720             pos.push({
31721                 x : x + (this.unitWidth + this.gutter) * 1,
31722                 y : y
31723             });
31724             
31725             return pos;
31726             
31727         }
31728         
31729         pos.push({
31730             x : x,
31731             y : y
31732         });
31733
31734         pos.push({
31735             x : x + (this.unitWidth + this.gutter) * 2,
31736             y : y
31737         });
31738
31739         pos.push({
31740             x : x + (this.unitWidth + this.gutter) * 2,
31741             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31742         });
31743             
31744         return pos;
31745         
31746     },
31747     
31748     getVerticalFourBoxColPositions : function(x, y, box)
31749     {
31750         var pos = [];
31751         
31752         if(box[0].size == 'xs'){
31753             
31754             pos.push({
31755                 x : x,
31756                 y : y
31757             });
31758
31759             pos.push({
31760                 x : x,
31761                 y : y + (this.unitHeight + this.gutter) * 1
31762             });
31763             
31764             pos.push({
31765                 x : x,
31766                 y : y + (this.unitHeight + this.gutter) * 2
31767             });
31768             
31769             pos.push({
31770                 x : x + (this.unitWidth + this.gutter) * 1,
31771                 y : y
31772             });
31773             
31774             return pos;
31775             
31776         }
31777         
31778         pos.push({
31779             x : x,
31780             y : y
31781         });
31782
31783         pos.push({
31784             x : x + (this.unitWidth + this.gutter) * 2,
31785             y : y
31786         });
31787
31788         pos.push({
31789             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31790             y : y + (this.unitHeight + this.gutter) * 1
31791         });
31792
31793         pos.push({
31794             x : x + (this.unitWidth + this.gutter) * 2,
31795             y : y + (this.unitWidth + this.gutter) * 2
31796         });
31797
31798         return pos;
31799         
31800     },
31801     
31802     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31803     {
31804         var pos = [];
31805         
31806         if(box[0].size == 'md-left'){
31807             pos.push({
31808                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31809                 y : minY
31810             });
31811             
31812             return pos;
31813         }
31814         
31815         if(box[0].size == 'md-right'){
31816             pos.push({
31817                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31818                 y : minY + (this.unitWidth + this.gutter) * 1
31819             });
31820             
31821             return pos;
31822         }
31823         
31824         var rand = Math.floor(Math.random() * (4 - box[0].y));
31825         
31826         pos.push({
31827             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31828             y : minY + (this.unitWidth + this.gutter) * rand
31829         });
31830         
31831         return pos;
31832         
31833     },
31834     
31835     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31836     {
31837         var pos = [];
31838         
31839         if(box[0].size == 'xs'){
31840             
31841             pos.push({
31842                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31843                 y : minY
31844             });
31845
31846             pos.push({
31847                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31848                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31849             });
31850             
31851             return pos;
31852             
31853         }
31854         
31855         pos.push({
31856             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31857             y : minY
31858         });
31859
31860         pos.push({
31861             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31862             y : minY + (this.unitWidth + this.gutter) * 2
31863         });
31864         
31865         return pos;
31866         
31867     },
31868     
31869     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31870     {
31871         var pos = [];
31872         
31873         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31874             
31875             pos.push({
31876                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31877                 y : minY
31878             });
31879
31880             pos.push({
31881                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31882                 y : minY + (this.unitWidth + this.gutter) * 1
31883             });
31884             
31885             pos.push({
31886                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31887                 y : minY + (this.unitWidth + this.gutter) * 2
31888             });
31889             
31890             return pos;
31891             
31892         }
31893         
31894         if(box[0].size == 'xs' && box[1].size == 'xs'){
31895             
31896             pos.push({
31897                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31898                 y : minY
31899             });
31900
31901             pos.push({
31902                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31903                 y : minY
31904             });
31905             
31906             pos.push({
31907                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31908                 y : minY + (this.unitWidth + this.gutter) * 1
31909             });
31910             
31911             return pos;
31912             
31913         }
31914         
31915         pos.push({
31916             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31917             y : minY
31918         });
31919
31920         pos.push({
31921             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31922             y : minY + (this.unitWidth + this.gutter) * 2
31923         });
31924
31925         pos.push({
31926             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31927             y : minY + (this.unitWidth + this.gutter) * 2
31928         });
31929             
31930         return pos;
31931         
31932     },
31933     
31934     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31935     {
31936         var pos = [];
31937         
31938         if(box[0].size == 'xs'){
31939             
31940             pos.push({
31941                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31942                 y : minY
31943             });
31944
31945             pos.push({
31946                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31947                 y : minY
31948             });
31949             
31950             pos.push({
31951                 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),
31952                 y : minY
31953             });
31954             
31955             pos.push({
31956                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31957                 y : minY + (this.unitWidth + this.gutter) * 1
31958             });
31959             
31960             return pos;
31961             
31962         }
31963         
31964         pos.push({
31965             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31966             y : minY
31967         });
31968         
31969         pos.push({
31970             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31971             y : minY + (this.unitWidth + this.gutter) * 2
31972         });
31973         
31974         pos.push({
31975             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31976             y : minY + (this.unitWidth + this.gutter) * 2
31977         });
31978         
31979         pos.push({
31980             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),
31981             y : minY + (this.unitWidth + this.gutter) * 2
31982         });
31983
31984         return pos;
31985         
31986     },
31987     
31988     /**
31989     * remove a Masonry Brick
31990     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31991     */
31992     removeBrick : function(brick_id)
31993     {
31994         if (!brick_id) {
31995             return;
31996         }
31997         
31998         for (var i = 0; i<this.bricks.length; i++) {
31999             if (this.bricks[i].id == brick_id) {
32000                 this.bricks.splice(i,1);
32001                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32002                 this.initial();
32003             }
32004         }
32005     },
32006     
32007     /**
32008     * adds a Masonry Brick
32009     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32010     */
32011     addBrick : function(cfg)
32012     {
32013         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32014         //this.register(cn);
32015         cn.parentId = this.id;
32016         cn.render(this.el);
32017         return cn;
32018     },
32019     
32020     /**
32021     * register a Masonry Brick
32022     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32023     */
32024     
32025     register : function(brick)
32026     {
32027         this.bricks.push(brick);
32028         brick.masonryId = this.id;
32029     },
32030     
32031     /**
32032     * clear all the Masonry Brick
32033     */
32034     clearAll : function()
32035     {
32036         this.bricks = [];
32037         //this.getChildContainer().dom.innerHTML = "";
32038         this.el.dom.innerHTML = '';
32039     },
32040     
32041     getSelected : function()
32042     {
32043         if (!this.selectedBrick) {
32044             return false;
32045         }
32046         
32047         return this.selectedBrick;
32048     }
32049 });
32050
32051 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32052     
32053     groups: {},
32054      /**
32055     * register a Masonry Layout
32056     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32057     */
32058     
32059     register : function(layout)
32060     {
32061         this.groups[layout.id] = layout;
32062     },
32063     /**
32064     * fetch a  Masonry Layout based on the masonry layout ID
32065     * @param {string} the masonry layout to add
32066     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32067     */
32068     
32069     get: function(layout_id) {
32070         if (typeof(this.groups[layout_id]) == 'undefined') {
32071             return false;
32072         }
32073         return this.groups[layout_id] ;
32074     }
32075     
32076     
32077     
32078 });
32079
32080  
32081
32082  /**
32083  *
32084  * This is based on 
32085  * http://masonry.desandro.com
32086  *
32087  * The idea is to render all the bricks based on vertical width...
32088  *
32089  * The original code extends 'outlayer' - we might need to use that....
32090  * 
32091  */
32092
32093
32094 /**
32095  * @class Roo.bootstrap.LayoutMasonryAuto
32096  * @extends Roo.bootstrap.Component
32097  * Bootstrap Layout Masonry class
32098  * 
32099  * @constructor
32100  * Create a new Element
32101  * @param {Object} config The config object
32102  */
32103
32104 Roo.bootstrap.LayoutMasonryAuto = function(config){
32105     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32106 };
32107
32108 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32109     
32110       /**
32111      * @cfg {Boolean} isFitWidth  - resize the width..
32112      */   
32113     isFitWidth : false,  // options..
32114     /**
32115      * @cfg {Boolean} isOriginLeft = left align?
32116      */   
32117     isOriginLeft : true,
32118     /**
32119      * @cfg {Boolean} isOriginTop = top align?
32120      */   
32121     isOriginTop : false,
32122     /**
32123      * @cfg {Boolean} isLayoutInstant = no animation?
32124      */   
32125     isLayoutInstant : false, // needed?
32126     /**
32127      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32128      */   
32129     isResizingContainer : true,
32130     /**
32131      * @cfg {Number} columnWidth  width of the columns 
32132      */   
32133     
32134     columnWidth : 0,
32135     
32136     /**
32137      * @cfg {Number} maxCols maximum number of columns
32138      */   
32139     
32140     maxCols: 0,
32141     /**
32142      * @cfg {Number} padHeight padding below box..
32143      */   
32144     
32145     padHeight : 10, 
32146     
32147     /**
32148      * @cfg {Boolean} isAutoInitial defalut true
32149      */   
32150     
32151     isAutoInitial : true, 
32152     
32153     // private?
32154     gutter : 0,
32155     
32156     containerWidth: 0,
32157     initialColumnWidth : 0,
32158     currentSize : null,
32159     
32160     colYs : null, // array.
32161     maxY : 0,
32162     padWidth: 10,
32163     
32164     
32165     tag: 'div',
32166     cls: '',
32167     bricks: null, //CompositeElement
32168     cols : 0, // array?
32169     // element : null, // wrapped now this.el
32170     _isLayoutInited : null, 
32171     
32172     
32173     getAutoCreate : function(){
32174         
32175         var cfg = {
32176             tag: this.tag,
32177             cls: 'blog-masonary-wrapper ' + this.cls,
32178             cn : {
32179                 cls : 'mas-boxes masonary'
32180             }
32181         };
32182         
32183         return cfg;
32184     },
32185     
32186     getChildContainer: function( )
32187     {
32188         if (this.boxesEl) {
32189             return this.boxesEl;
32190         }
32191         
32192         this.boxesEl = this.el.select('.mas-boxes').first();
32193         
32194         return this.boxesEl;
32195     },
32196     
32197     
32198     initEvents : function()
32199     {
32200         var _this = this;
32201         
32202         if(this.isAutoInitial){
32203             Roo.log('hook children rendered');
32204             this.on('childrenrendered', function() {
32205                 Roo.log('children rendered');
32206                 _this.initial();
32207             } ,this);
32208         }
32209         
32210     },
32211     
32212     initial : function()
32213     {
32214         this.reloadItems();
32215
32216         this.currentSize = this.el.getBox(true);
32217
32218         /// was window resize... - let's see if this works..
32219         Roo.EventManager.onWindowResize(this.resize, this); 
32220
32221         if(!this.isAutoInitial){
32222             this.layout();
32223             return;
32224         }
32225         
32226         this.layout.defer(500,this);
32227     },
32228     
32229     reloadItems: function()
32230     {
32231         this.bricks = this.el.select('.masonry-brick', true);
32232         
32233         this.bricks.each(function(b) {
32234             //Roo.log(b.getSize());
32235             if (!b.attr('originalwidth')) {
32236                 b.attr('originalwidth',  b.getSize().width);
32237             }
32238             
32239         });
32240         
32241         Roo.log(this.bricks.elements.length);
32242     },
32243     
32244     resize : function()
32245     {
32246         Roo.log('resize');
32247         var cs = this.el.getBox(true);
32248         
32249         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32250             Roo.log("no change in with or X");
32251             return;
32252         }
32253         this.currentSize = cs;
32254         this.layout();
32255     },
32256     
32257     layout : function()
32258     {
32259          Roo.log('layout');
32260         this._resetLayout();
32261         //this._manageStamps();
32262       
32263         // don't animate first layout
32264         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32265         this.layoutItems( isInstant );
32266       
32267         // flag for initalized
32268         this._isLayoutInited = true;
32269     },
32270     
32271     layoutItems : function( isInstant )
32272     {
32273         //var items = this._getItemsForLayout( this.items );
32274         // original code supports filtering layout items.. we just ignore it..
32275         
32276         this._layoutItems( this.bricks , isInstant );
32277       
32278         this._postLayout();
32279     },
32280     _layoutItems : function ( items , isInstant)
32281     {
32282        //this.fireEvent( 'layout', this, items );
32283     
32284
32285         if ( !items || !items.elements.length ) {
32286           // no items, emit event with empty array
32287             return;
32288         }
32289
32290         var queue = [];
32291         items.each(function(item) {
32292             Roo.log("layout item");
32293             Roo.log(item);
32294             // get x/y object from method
32295             var position = this._getItemLayoutPosition( item );
32296             // enqueue
32297             position.item = item;
32298             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32299             queue.push( position );
32300         }, this);
32301       
32302         this._processLayoutQueue( queue );
32303     },
32304     /** Sets position of item in DOM
32305     * @param {Element} item
32306     * @param {Number} x - horizontal position
32307     * @param {Number} y - vertical position
32308     * @param {Boolean} isInstant - disables transitions
32309     */
32310     _processLayoutQueue : function( queue )
32311     {
32312         for ( var i=0, len = queue.length; i < len; i++ ) {
32313             var obj = queue[i];
32314             obj.item.position('absolute');
32315             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32316         }
32317     },
32318       
32319     
32320     /**
32321     * Any logic you want to do after each layout,
32322     * i.e. size the container
32323     */
32324     _postLayout : function()
32325     {
32326         this.resizeContainer();
32327     },
32328     
32329     resizeContainer : function()
32330     {
32331         if ( !this.isResizingContainer ) {
32332             return;
32333         }
32334         var size = this._getContainerSize();
32335         if ( size ) {
32336             this.el.setSize(size.width,size.height);
32337             this.boxesEl.setSize(size.width,size.height);
32338         }
32339     },
32340     
32341     
32342     
32343     _resetLayout : function()
32344     {
32345         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32346         this.colWidth = this.el.getWidth();
32347         //this.gutter = this.el.getWidth(); 
32348         
32349         this.measureColumns();
32350
32351         // reset column Y
32352         var i = this.cols;
32353         this.colYs = [];
32354         while (i--) {
32355             this.colYs.push( 0 );
32356         }
32357     
32358         this.maxY = 0;
32359     },
32360
32361     measureColumns : function()
32362     {
32363         this.getContainerWidth();
32364       // if columnWidth is 0, default to outerWidth of first item
32365         if ( !this.columnWidth ) {
32366             var firstItem = this.bricks.first();
32367             Roo.log(firstItem);
32368             this.columnWidth  = this.containerWidth;
32369             if (firstItem && firstItem.attr('originalwidth') ) {
32370                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32371             }
32372             // columnWidth fall back to item of first element
32373             Roo.log("set column width?");
32374                         this.initialColumnWidth = this.columnWidth  ;
32375
32376             // if first elem has no width, default to size of container
32377             
32378         }
32379         
32380         
32381         if (this.initialColumnWidth) {
32382             this.columnWidth = this.initialColumnWidth;
32383         }
32384         
32385         
32386             
32387         // column width is fixed at the top - however if container width get's smaller we should
32388         // reduce it...
32389         
32390         // this bit calcs how man columns..
32391             
32392         var columnWidth = this.columnWidth += this.gutter;
32393       
32394         // calculate columns
32395         var containerWidth = this.containerWidth + this.gutter;
32396         
32397         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32398         // fix rounding errors, typically with gutters
32399         var excess = columnWidth - containerWidth % columnWidth;
32400         
32401         
32402         // if overshoot is less than a pixel, round up, otherwise floor it
32403         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32404         cols = Math[ mathMethod ]( cols );
32405         this.cols = Math.max( cols, 1 );
32406         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32407         
32408          // padding positioning..
32409         var totalColWidth = this.cols * this.columnWidth;
32410         var padavail = this.containerWidth - totalColWidth;
32411         // so for 2 columns - we need 3 'pads'
32412         
32413         var padNeeded = (1+this.cols) * this.padWidth;
32414         
32415         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32416         
32417         this.columnWidth += padExtra
32418         //this.padWidth = Math.floor(padavail /  ( this.cols));
32419         
32420         // adjust colum width so that padding is fixed??
32421         
32422         // we have 3 columns ... total = width * 3
32423         // we have X left over... that should be used by 
32424         
32425         //if (this.expandC) {
32426             
32427         //}
32428         
32429         
32430         
32431     },
32432     
32433     getContainerWidth : function()
32434     {
32435        /* // container is parent if fit width
32436         var container = this.isFitWidth ? this.element.parentNode : this.element;
32437         // check that this.size and size are there
32438         // IE8 triggers resize on body size change, so they might not be
32439         
32440         var size = getSize( container );  //FIXME
32441         this.containerWidth = size && size.innerWidth; //FIXME
32442         */
32443          
32444         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32445         
32446     },
32447     
32448     _getItemLayoutPosition : function( item )  // what is item?
32449     {
32450         // we resize the item to our columnWidth..
32451       
32452         item.setWidth(this.columnWidth);
32453         item.autoBoxAdjust  = false;
32454         
32455         var sz = item.getSize();
32456  
32457         // how many columns does this brick span
32458         var remainder = this.containerWidth % this.columnWidth;
32459         
32460         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32461         // round if off by 1 pixel, otherwise use ceil
32462         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32463         colSpan = Math.min( colSpan, this.cols );
32464         
32465         // normally this should be '1' as we dont' currently allow multi width columns..
32466         
32467         var colGroup = this._getColGroup( colSpan );
32468         // get the minimum Y value from the columns
32469         var minimumY = Math.min.apply( Math, colGroup );
32470         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32471         
32472         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32473          
32474         // position the brick
32475         var position = {
32476             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32477             y: this.currentSize.y + minimumY + this.padHeight
32478         };
32479         
32480         Roo.log(position);
32481         // apply setHeight to necessary columns
32482         var setHeight = minimumY + sz.height + this.padHeight;
32483         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32484         
32485         var setSpan = this.cols + 1 - colGroup.length;
32486         for ( var i = 0; i < setSpan; i++ ) {
32487           this.colYs[ shortColIndex + i ] = setHeight ;
32488         }
32489       
32490         return position;
32491     },
32492     
32493     /**
32494      * @param {Number} colSpan - number of columns the element spans
32495      * @returns {Array} colGroup
32496      */
32497     _getColGroup : function( colSpan )
32498     {
32499         if ( colSpan < 2 ) {
32500           // if brick spans only one column, use all the column Ys
32501           return this.colYs;
32502         }
32503       
32504         var colGroup = [];
32505         // how many different places could this brick fit horizontally
32506         var groupCount = this.cols + 1 - colSpan;
32507         // for each group potential horizontal position
32508         for ( var i = 0; i < groupCount; i++ ) {
32509           // make an array of colY values for that one group
32510           var groupColYs = this.colYs.slice( i, i + colSpan );
32511           // and get the max value of the array
32512           colGroup[i] = Math.max.apply( Math, groupColYs );
32513         }
32514         return colGroup;
32515     },
32516     /*
32517     _manageStamp : function( stamp )
32518     {
32519         var stampSize =  stamp.getSize();
32520         var offset = stamp.getBox();
32521         // get the columns that this stamp affects
32522         var firstX = this.isOriginLeft ? offset.x : offset.right;
32523         var lastX = firstX + stampSize.width;
32524         var firstCol = Math.floor( firstX / this.columnWidth );
32525         firstCol = Math.max( 0, firstCol );
32526         
32527         var lastCol = Math.floor( lastX / this.columnWidth );
32528         // lastCol should not go over if multiple of columnWidth #425
32529         lastCol -= lastX % this.columnWidth ? 0 : 1;
32530         lastCol = Math.min( this.cols - 1, lastCol );
32531         
32532         // set colYs to bottom of the stamp
32533         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32534             stampSize.height;
32535             
32536         for ( var i = firstCol; i <= lastCol; i++ ) {
32537           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32538         }
32539     },
32540     */
32541     
32542     _getContainerSize : function()
32543     {
32544         this.maxY = Math.max.apply( Math, this.colYs );
32545         var size = {
32546             height: this.maxY
32547         };
32548       
32549         if ( this.isFitWidth ) {
32550             size.width = this._getContainerFitWidth();
32551         }
32552       
32553         return size;
32554     },
32555     
32556     _getContainerFitWidth : function()
32557     {
32558         var unusedCols = 0;
32559         // count unused columns
32560         var i = this.cols;
32561         while ( --i ) {
32562           if ( this.colYs[i] !== 0 ) {
32563             break;
32564           }
32565           unusedCols++;
32566         }
32567         // fit container to columns that have been used
32568         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32569     },
32570     
32571     needsResizeLayout : function()
32572     {
32573         var previousWidth = this.containerWidth;
32574         this.getContainerWidth();
32575         return previousWidth !== this.containerWidth;
32576     }
32577  
32578 });
32579
32580  
32581
32582  /*
32583  * - LGPL
32584  *
32585  * element
32586  * 
32587  */
32588
32589 /**
32590  * @class Roo.bootstrap.MasonryBrick
32591  * @extends Roo.bootstrap.Component
32592  * Bootstrap MasonryBrick class
32593  * 
32594  * @constructor
32595  * Create a new MasonryBrick
32596  * @param {Object} config The config object
32597  */
32598
32599 Roo.bootstrap.MasonryBrick = function(config){
32600     
32601     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32602     
32603     Roo.bootstrap.MasonryBrick.register(this);
32604     
32605     this.addEvents({
32606         // raw events
32607         /**
32608          * @event click
32609          * When a MasonryBrick is clcik
32610          * @param {Roo.bootstrap.MasonryBrick} this
32611          * @param {Roo.EventObject} e
32612          */
32613         "click" : true
32614     });
32615 };
32616
32617 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32618     
32619     /**
32620      * @cfg {String} title
32621      */   
32622     title : '',
32623     /**
32624      * @cfg {String} html
32625      */   
32626     html : '',
32627     /**
32628      * @cfg {String} bgimage
32629      */   
32630     bgimage : '',
32631     /**
32632      * @cfg {String} videourl
32633      */   
32634     videourl : '',
32635     /**
32636      * @cfg {String} cls
32637      */   
32638     cls : '',
32639     /**
32640      * @cfg {String} href
32641      */   
32642     href : '',
32643     /**
32644      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32645      */   
32646     size : 'xs',
32647     
32648     /**
32649      * @cfg {String} placetitle (center|bottom)
32650      */   
32651     placetitle : '',
32652     
32653     /**
32654      * @cfg {Boolean} isFitContainer defalut true
32655      */   
32656     isFitContainer : true, 
32657     
32658     /**
32659      * @cfg {Boolean} preventDefault defalut false
32660      */   
32661     preventDefault : false, 
32662     
32663     /**
32664      * @cfg {Boolean} inverse defalut false
32665      */   
32666     maskInverse : false, 
32667     
32668     getAutoCreate : function()
32669     {
32670         if(!this.isFitContainer){
32671             return this.getSplitAutoCreate();
32672         }
32673         
32674         var cls = 'masonry-brick masonry-brick-full';
32675         
32676         if(this.href.length){
32677             cls += ' masonry-brick-link';
32678         }
32679         
32680         if(this.bgimage.length){
32681             cls += ' masonry-brick-image';
32682         }
32683         
32684         if(this.maskInverse){
32685             cls += ' mask-inverse';
32686         }
32687         
32688         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32689             cls += ' enable-mask';
32690         }
32691         
32692         if(this.size){
32693             cls += ' masonry-' + this.size + '-brick';
32694         }
32695         
32696         if(this.placetitle.length){
32697             
32698             switch (this.placetitle) {
32699                 case 'center' :
32700                     cls += ' masonry-center-title';
32701                     break;
32702                 case 'bottom' :
32703                     cls += ' masonry-bottom-title';
32704                     break;
32705                 default:
32706                     break;
32707             }
32708             
32709         } else {
32710             if(!this.html.length && !this.bgimage.length){
32711                 cls += ' masonry-center-title';
32712             }
32713
32714             if(!this.html.length && this.bgimage.length){
32715                 cls += ' masonry-bottom-title';
32716             }
32717         }
32718         
32719         if(this.cls){
32720             cls += ' ' + this.cls;
32721         }
32722         
32723         var cfg = {
32724             tag: (this.href.length) ? 'a' : 'div',
32725             cls: cls,
32726             cn: [
32727                 {
32728                     tag: 'div',
32729                     cls: 'masonry-brick-mask'
32730                 },
32731                 {
32732                     tag: 'div',
32733                     cls: 'masonry-brick-paragraph',
32734                     cn: []
32735                 }
32736             ]
32737         };
32738         
32739         if(this.href.length){
32740             cfg.href = this.href;
32741         }
32742         
32743         var cn = cfg.cn[1].cn;
32744         
32745         if(this.title.length){
32746             cn.push({
32747                 tag: 'h4',
32748                 cls: 'masonry-brick-title',
32749                 html: this.title
32750             });
32751         }
32752         
32753         if(this.html.length){
32754             cn.push({
32755                 tag: 'p',
32756                 cls: 'masonry-brick-text',
32757                 html: this.html
32758             });
32759         }
32760         
32761         if (!this.title.length && !this.html.length) {
32762             cfg.cn[1].cls += ' hide';
32763         }
32764         
32765         if(this.bgimage.length){
32766             cfg.cn.push({
32767                 tag: 'img',
32768                 cls: 'masonry-brick-image-view',
32769                 src: this.bgimage
32770             });
32771         }
32772         
32773         if(this.videourl.length){
32774             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32775             // youtube support only?
32776             cfg.cn.push({
32777                 tag: 'iframe',
32778                 cls: 'masonry-brick-image-view',
32779                 src: vurl,
32780                 frameborder : 0,
32781                 allowfullscreen : true
32782             });
32783         }
32784         
32785         return cfg;
32786         
32787     },
32788     
32789     getSplitAutoCreate : function()
32790     {
32791         var cls = 'masonry-brick masonry-brick-split';
32792         
32793         if(this.href.length){
32794             cls += ' masonry-brick-link';
32795         }
32796         
32797         if(this.bgimage.length){
32798             cls += ' masonry-brick-image';
32799         }
32800         
32801         if(this.size){
32802             cls += ' masonry-' + this.size + '-brick';
32803         }
32804         
32805         switch (this.placetitle) {
32806             case 'center' :
32807                 cls += ' masonry-center-title';
32808                 break;
32809             case 'bottom' :
32810                 cls += ' masonry-bottom-title';
32811                 break;
32812             default:
32813                 if(!this.bgimage.length){
32814                     cls += ' masonry-center-title';
32815                 }
32816
32817                 if(this.bgimage.length){
32818                     cls += ' masonry-bottom-title';
32819                 }
32820                 break;
32821         }
32822         
32823         if(this.cls){
32824             cls += ' ' + this.cls;
32825         }
32826         
32827         var cfg = {
32828             tag: (this.href.length) ? 'a' : 'div',
32829             cls: cls,
32830             cn: [
32831                 {
32832                     tag: 'div',
32833                     cls: 'masonry-brick-split-head',
32834                     cn: [
32835                         {
32836                             tag: 'div',
32837                             cls: 'masonry-brick-paragraph',
32838                             cn: []
32839                         }
32840                     ]
32841                 },
32842                 {
32843                     tag: 'div',
32844                     cls: 'masonry-brick-split-body',
32845                     cn: []
32846                 }
32847             ]
32848         };
32849         
32850         if(this.href.length){
32851             cfg.href = this.href;
32852         }
32853         
32854         if(this.title.length){
32855             cfg.cn[0].cn[0].cn.push({
32856                 tag: 'h4',
32857                 cls: 'masonry-brick-title',
32858                 html: this.title
32859             });
32860         }
32861         
32862         if(this.html.length){
32863             cfg.cn[1].cn.push({
32864                 tag: 'p',
32865                 cls: 'masonry-brick-text',
32866                 html: this.html
32867             });
32868         }
32869
32870         if(this.bgimage.length){
32871             cfg.cn[0].cn.push({
32872                 tag: 'img',
32873                 cls: 'masonry-brick-image-view',
32874                 src: this.bgimage
32875             });
32876         }
32877         
32878         if(this.videourl.length){
32879             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32880             // youtube support only?
32881             cfg.cn[0].cn.cn.push({
32882                 tag: 'iframe',
32883                 cls: 'masonry-brick-image-view',
32884                 src: vurl,
32885                 frameborder : 0,
32886                 allowfullscreen : true
32887             });
32888         }
32889         
32890         return cfg;
32891     },
32892     
32893     initEvents: function() 
32894     {
32895         switch (this.size) {
32896             case 'xs' :
32897                 this.x = 1;
32898                 this.y = 1;
32899                 break;
32900             case 'sm' :
32901                 this.x = 2;
32902                 this.y = 2;
32903                 break;
32904             case 'md' :
32905             case 'md-left' :
32906             case 'md-right' :
32907                 this.x = 3;
32908                 this.y = 3;
32909                 break;
32910             case 'tall' :
32911                 this.x = 2;
32912                 this.y = 3;
32913                 break;
32914             case 'wide' :
32915                 this.x = 3;
32916                 this.y = 2;
32917                 break;
32918             case 'wide-thin' :
32919                 this.x = 3;
32920                 this.y = 1;
32921                 break;
32922                         
32923             default :
32924                 break;
32925         }
32926         
32927         if(Roo.isTouch){
32928             this.el.on('touchstart', this.onTouchStart, this);
32929             this.el.on('touchmove', this.onTouchMove, this);
32930             this.el.on('touchend', this.onTouchEnd, this);
32931             this.el.on('contextmenu', this.onContextMenu, this);
32932         } else {
32933             this.el.on('mouseenter'  ,this.enter, this);
32934             this.el.on('mouseleave', this.leave, this);
32935             this.el.on('click', this.onClick, this);
32936         }
32937         
32938         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32939             this.parent().bricks.push(this);   
32940         }
32941         
32942     },
32943     
32944     onClick: function(e, el)
32945     {
32946         var time = this.endTimer - this.startTimer;
32947         // Roo.log(e.preventDefault());
32948         if(Roo.isTouch){
32949             if(time > 1000){
32950                 e.preventDefault();
32951                 return;
32952             }
32953         }
32954         
32955         if(!this.preventDefault){
32956             return;
32957         }
32958         
32959         e.preventDefault();
32960         
32961         if (this.activeClass != '') {
32962             this.selectBrick();
32963         }
32964         
32965         this.fireEvent('click', this, e);
32966     },
32967     
32968     enter: function(e, el)
32969     {
32970         e.preventDefault();
32971         
32972         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32973             return;
32974         }
32975         
32976         if(this.bgimage.length && this.html.length){
32977             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32978         }
32979     },
32980     
32981     leave: function(e, el)
32982     {
32983         e.preventDefault();
32984         
32985         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32986             return;
32987         }
32988         
32989         if(this.bgimage.length && this.html.length){
32990             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32991         }
32992     },
32993     
32994     onTouchStart: function(e, el)
32995     {
32996 //        e.preventDefault();
32997         
32998         this.touchmoved = false;
32999         
33000         if(!this.isFitContainer){
33001             return;
33002         }
33003         
33004         if(!this.bgimage.length || !this.html.length){
33005             return;
33006         }
33007         
33008         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33009         
33010         this.timer = new Date().getTime();
33011         
33012     },
33013     
33014     onTouchMove: function(e, el)
33015     {
33016         this.touchmoved = true;
33017     },
33018     
33019     onContextMenu : function(e,el)
33020     {
33021         e.preventDefault();
33022         e.stopPropagation();
33023         return false;
33024     },
33025     
33026     onTouchEnd: function(e, el)
33027     {
33028 //        e.preventDefault();
33029         
33030         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33031         
33032             this.leave(e,el);
33033             
33034             return;
33035         }
33036         
33037         if(!this.bgimage.length || !this.html.length){
33038             
33039             if(this.href.length){
33040                 window.location.href = this.href;
33041             }
33042             
33043             return;
33044         }
33045         
33046         if(!this.isFitContainer){
33047             return;
33048         }
33049         
33050         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33051         
33052         window.location.href = this.href;
33053     },
33054     
33055     //selection on single brick only
33056     selectBrick : function() {
33057         
33058         if (!this.parentId) {
33059             return;
33060         }
33061         
33062         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33063         var index = m.selectedBrick.indexOf(this.id);
33064         
33065         if ( index > -1) {
33066             m.selectedBrick.splice(index,1);
33067             this.el.removeClass(this.activeClass);
33068             return;
33069         }
33070         
33071         for(var i = 0; i < m.selectedBrick.length; i++) {
33072             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33073             b.el.removeClass(b.activeClass);
33074         }
33075         
33076         m.selectedBrick = [];
33077         
33078         m.selectedBrick.push(this.id);
33079         this.el.addClass(this.activeClass);
33080         return;
33081     },
33082     
33083     isSelected : function(){
33084         return this.el.hasClass(this.activeClass);
33085         
33086     }
33087 });
33088
33089 Roo.apply(Roo.bootstrap.MasonryBrick, {
33090     
33091     //groups: {},
33092     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33093      /**
33094     * register a Masonry Brick
33095     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33096     */
33097     
33098     register : function(brick)
33099     {
33100         //this.groups[brick.id] = brick;
33101         this.groups.add(brick.id, brick);
33102     },
33103     /**
33104     * fetch a  masonry brick based on the masonry brick ID
33105     * @param {string} the masonry brick to add
33106     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33107     */
33108     
33109     get: function(brick_id) 
33110     {
33111         // if (typeof(this.groups[brick_id]) == 'undefined') {
33112         //     return false;
33113         // }
33114         // return this.groups[brick_id] ;
33115         
33116         if(this.groups.key(brick_id)) {
33117             return this.groups.key(brick_id);
33118         }
33119         
33120         return false;
33121     }
33122     
33123     
33124     
33125 });
33126
33127  /*
33128  * - LGPL
33129  *
33130  * element
33131  * 
33132  */
33133
33134 /**
33135  * @class Roo.bootstrap.Brick
33136  * @extends Roo.bootstrap.Component
33137  * Bootstrap Brick class
33138  * 
33139  * @constructor
33140  * Create a new Brick
33141  * @param {Object} config The config object
33142  */
33143
33144 Roo.bootstrap.Brick = function(config){
33145     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33146     
33147     this.addEvents({
33148         // raw events
33149         /**
33150          * @event click
33151          * When a Brick is click
33152          * @param {Roo.bootstrap.Brick} this
33153          * @param {Roo.EventObject} e
33154          */
33155         "click" : true
33156     });
33157 };
33158
33159 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33160     
33161     /**
33162      * @cfg {String} title
33163      */   
33164     title : '',
33165     /**
33166      * @cfg {String} html
33167      */   
33168     html : '',
33169     /**
33170      * @cfg {String} bgimage
33171      */   
33172     bgimage : '',
33173     /**
33174      * @cfg {String} cls
33175      */   
33176     cls : '',
33177     /**
33178      * @cfg {String} href
33179      */   
33180     href : '',
33181     /**
33182      * @cfg {String} video
33183      */   
33184     video : '',
33185     /**
33186      * @cfg {Boolean} square
33187      */   
33188     square : true,
33189     
33190     getAutoCreate : function()
33191     {
33192         var cls = 'roo-brick';
33193         
33194         if(this.href.length){
33195             cls += ' roo-brick-link';
33196         }
33197         
33198         if(this.bgimage.length){
33199             cls += ' roo-brick-image';
33200         }
33201         
33202         if(!this.html.length && !this.bgimage.length){
33203             cls += ' roo-brick-center-title';
33204         }
33205         
33206         if(!this.html.length && this.bgimage.length){
33207             cls += ' roo-brick-bottom-title';
33208         }
33209         
33210         if(this.cls){
33211             cls += ' ' + this.cls;
33212         }
33213         
33214         var cfg = {
33215             tag: (this.href.length) ? 'a' : 'div',
33216             cls: cls,
33217             cn: [
33218                 {
33219                     tag: 'div',
33220                     cls: 'roo-brick-paragraph',
33221                     cn: []
33222                 }
33223             ]
33224         };
33225         
33226         if(this.href.length){
33227             cfg.href = this.href;
33228         }
33229         
33230         var cn = cfg.cn[0].cn;
33231         
33232         if(this.title.length){
33233             cn.push({
33234                 tag: 'h4',
33235                 cls: 'roo-brick-title',
33236                 html: this.title
33237             });
33238         }
33239         
33240         if(this.html.length){
33241             cn.push({
33242                 tag: 'p',
33243                 cls: 'roo-brick-text',
33244                 html: this.html
33245             });
33246         } else {
33247             cn.cls += ' hide';
33248         }
33249         
33250         if(this.bgimage.length){
33251             cfg.cn.push({
33252                 tag: 'img',
33253                 cls: 'roo-brick-image-view',
33254                 src: this.bgimage
33255             });
33256         }
33257         
33258         return cfg;
33259     },
33260     
33261     initEvents: function() 
33262     {
33263         if(this.title.length || this.html.length){
33264             this.el.on('mouseenter'  ,this.enter, this);
33265             this.el.on('mouseleave', this.leave, this);
33266         }
33267         
33268         Roo.EventManager.onWindowResize(this.resize, this); 
33269         
33270         if(this.bgimage.length){
33271             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33272             this.imageEl.on('load', this.onImageLoad, this);
33273             return;
33274         }
33275         
33276         this.resize();
33277     },
33278     
33279     onImageLoad : function()
33280     {
33281         this.resize();
33282     },
33283     
33284     resize : function()
33285     {
33286         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33287         
33288         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33289         
33290         if(this.bgimage.length){
33291             var image = this.el.select('.roo-brick-image-view', true).first();
33292             
33293             image.setWidth(paragraph.getWidth());
33294             
33295             if(this.square){
33296                 image.setHeight(paragraph.getWidth());
33297             }
33298             
33299             this.el.setHeight(image.getHeight());
33300             paragraph.setHeight(image.getHeight());
33301             
33302         }
33303         
33304     },
33305     
33306     enter: function(e, el)
33307     {
33308         e.preventDefault();
33309         
33310         if(this.bgimage.length){
33311             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33312             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33313         }
33314     },
33315     
33316     leave: function(e, el)
33317     {
33318         e.preventDefault();
33319         
33320         if(this.bgimage.length){
33321             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33322             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33323         }
33324     }
33325     
33326 });
33327
33328  
33329
33330  /*
33331  * - LGPL
33332  *
33333  * Number field 
33334  */
33335
33336 /**
33337  * @class Roo.bootstrap.NumberField
33338  * @extends Roo.bootstrap.Input
33339  * Bootstrap NumberField class
33340  * 
33341  * 
33342  * 
33343  * 
33344  * @constructor
33345  * Create a new NumberField
33346  * @param {Object} config The config object
33347  */
33348
33349 Roo.bootstrap.NumberField = function(config){
33350     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33351 };
33352
33353 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33354     
33355     /**
33356      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33357      */
33358     allowDecimals : true,
33359     /**
33360      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33361      */
33362     decimalSeparator : ".",
33363     /**
33364      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33365      */
33366     decimalPrecision : 2,
33367     /**
33368      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33369      */
33370     allowNegative : true,
33371     
33372     /**
33373      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33374      */
33375     allowZero: true,
33376     /**
33377      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33378      */
33379     minValue : Number.NEGATIVE_INFINITY,
33380     /**
33381      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33382      */
33383     maxValue : Number.MAX_VALUE,
33384     /**
33385      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33386      */
33387     minText : "The minimum value for this field is {0}",
33388     /**
33389      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33390      */
33391     maxText : "The maximum value for this field is {0}",
33392     /**
33393      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33394      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33395      */
33396     nanText : "{0} is not a valid number",
33397     /**
33398      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33399      */
33400     thousandsDelimiter : false,
33401     /**
33402      * @cfg {String} valueAlign alignment of value
33403      */
33404     valueAlign : "left",
33405
33406     getAutoCreate : function()
33407     {
33408         var hiddenInput = {
33409             tag: 'input',
33410             type: 'hidden',
33411             id: Roo.id(),
33412             cls: 'hidden-number-input'
33413         };
33414         
33415         if (this.name) {
33416             hiddenInput.name = this.name;
33417         }
33418         
33419         this.name = '';
33420         
33421         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33422         
33423         this.name = hiddenInput.name;
33424         
33425         if(cfg.cn.length > 0) {
33426             cfg.cn.push(hiddenInput);
33427         }
33428         
33429         return cfg;
33430     },
33431
33432     // private
33433     initEvents : function()
33434     {   
33435         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33436         
33437         var allowed = "0123456789";
33438         
33439         if(this.allowDecimals){
33440             allowed += this.decimalSeparator;
33441         }
33442         
33443         if(this.allowNegative){
33444             allowed += "-";
33445         }
33446         
33447         if(this.thousandsDelimiter) {
33448             allowed += ",";
33449         }
33450         
33451         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33452         
33453         var keyPress = function(e){
33454             
33455             var k = e.getKey();
33456             
33457             var c = e.getCharCode();
33458             
33459             if(
33460                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33461                     allowed.indexOf(String.fromCharCode(c)) === -1
33462             ){
33463                 e.stopEvent();
33464                 return;
33465             }
33466             
33467             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33468                 return;
33469             }
33470             
33471             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33472                 e.stopEvent();
33473             }
33474         };
33475         
33476         this.el.on("keypress", keyPress, this);
33477     },
33478     
33479     validateValue : function(value)
33480     {
33481         
33482         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33483             return false;
33484         }
33485         
33486         var num = this.parseValue(value);
33487         
33488         if(isNaN(num)){
33489             this.markInvalid(String.format(this.nanText, value));
33490             return false;
33491         }
33492         
33493         if(num < this.minValue){
33494             this.markInvalid(String.format(this.minText, this.minValue));
33495             return false;
33496         }
33497         
33498         if(num > this.maxValue){
33499             this.markInvalid(String.format(this.maxText, this.maxValue));
33500             return false;
33501         }
33502         
33503         return true;
33504     },
33505
33506     getValue : function()
33507     {
33508         var v = this.hiddenEl().getValue();
33509         
33510         return this.fixPrecision(this.parseValue(v));
33511     },
33512
33513     parseValue : function(value)
33514     {
33515         if(this.thousandsDelimiter) {
33516             value += "";
33517             r = new RegExp(",", "g");
33518             value = value.replace(r, "");
33519         }
33520         
33521         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33522         return isNaN(value) ? '' : value;
33523     },
33524
33525     fixPrecision : function(value)
33526     {
33527         if(this.thousandsDelimiter) {
33528             value += "";
33529             r = new RegExp(",", "g");
33530             value = value.replace(r, "");
33531         }
33532         
33533         var nan = isNaN(value);
33534         
33535         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33536             return nan ? '' : value;
33537         }
33538         return parseFloat(value).toFixed(this.decimalPrecision);
33539     },
33540
33541     setValue : function(v)
33542     {
33543         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33544         
33545         this.value = v;
33546         
33547         if(this.rendered){
33548             
33549             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33550             
33551             this.inputEl().dom.value = (v == '') ? '' :
33552                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33553             
33554             if(!this.allowZero && v === '0') {
33555                 this.hiddenEl().dom.value = '';
33556                 this.inputEl().dom.value = '';
33557             }
33558             
33559             this.validate();
33560         }
33561     },
33562
33563     decimalPrecisionFcn : function(v)
33564     {
33565         return Math.floor(v);
33566     },
33567
33568     beforeBlur : function()
33569     {
33570         var v = this.parseValue(this.getRawValue());
33571         
33572         if(v || v === 0 || v === ''){
33573             this.setValue(v);
33574         }
33575     },
33576     
33577     hiddenEl : function()
33578     {
33579         return this.el.select('input.hidden-number-input',true).first();
33580     }
33581     
33582 });
33583
33584  
33585
33586 /*
33587 * Licence: LGPL
33588 */
33589
33590 /**
33591  * @class Roo.bootstrap.DocumentSlider
33592  * @extends Roo.bootstrap.Component
33593  * Bootstrap DocumentSlider class
33594  * 
33595  * @constructor
33596  * Create a new DocumentViewer
33597  * @param {Object} config The config object
33598  */
33599
33600 Roo.bootstrap.DocumentSlider = function(config){
33601     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33602     
33603     this.files = [];
33604     
33605     this.addEvents({
33606         /**
33607          * @event initial
33608          * Fire after initEvent
33609          * @param {Roo.bootstrap.DocumentSlider} this
33610          */
33611         "initial" : true,
33612         /**
33613          * @event update
33614          * Fire after update
33615          * @param {Roo.bootstrap.DocumentSlider} this
33616          */
33617         "update" : true,
33618         /**
33619          * @event click
33620          * Fire after click
33621          * @param {Roo.bootstrap.DocumentSlider} this
33622          */
33623         "click" : true
33624     });
33625 };
33626
33627 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33628     
33629     files : false,
33630     
33631     indicator : 0,
33632     
33633     getAutoCreate : function()
33634     {
33635         var cfg = {
33636             tag : 'div',
33637             cls : 'roo-document-slider',
33638             cn : [
33639                 {
33640                     tag : 'div',
33641                     cls : 'roo-document-slider-header',
33642                     cn : [
33643                         {
33644                             tag : 'div',
33645                             cls : 'roo-document-slider-header-title'
33646                         }
33647                     ]
33648                 },
33649                 {
33650                     tag : 'div',
33651                     cls : 'roo-document-slider-body',
33652                     cn : [
33653                         {
33654                             tag : 'div',
33655                             cls : 'roo-document-slider-prev',
33656                             cn : [
33657                                 {
33658                                     tag : 'i',
33659                                     cls : 'fa fa-chevron-left'
33660                                 }
33661                             ]
33662                         },
33663                         {
33664                             tag : 'div',
33665                             cls : 'roo-document-slider-thumb',
33666                             cn : [
33667                                 {
33668                                     tag : 'img',
33669                                     cls : 'roo-document-slider-image'
33670                                 }
33671                             ]
33672                         },
33673                         {
33674                             tag : 'div',
33675                             cls : 'roo-document-slider-next',
33676                             cn : [
33677                                 {
33678                                     tag : 'i',
33679                                     cls : 'fa fa-chevron-right'
33680                                 }
33681                             ]
33682                         }
33683                     ]
33684                 }
33685             ]
33686         };
33687         
33688         return cfg;
33689     },
33690     
33691     initEvents : function()
33692     {
33693         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33694         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33695         
33696         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33697         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33698         
33699         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33700         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33701         
33702         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33703         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33704         
33705         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33706         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33707         
33708         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33709         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33710         
33711         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33712         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33713         
33714         this.thumbEl.on('click', this.onClick, this);
33715         
33716         this.prevIndicator.on('click', this.prev, this);
33717         
33718         this.nextIndicator.on('click', this.next, this);
33719         
33720     },
33721     
33722     initial : function()
33723     {
33724         if(this.files.length){
33725             this.indicator = 1;
33726             this.update()
33727         }
33728         
33729         this.fireEvent('initial', this);
33730     },
33731     
33732     update : function()
33733     {
33734         this.imageEl.attr('src', this.files[this.indicator - 1]);
33735         
33736         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33737         
33738         this.prevIndicator.show();
33739         
33740         if(this.indicator == 1){
33741             this.prevIndicator.hide();
33742         }
33743         
33744         this.nextIndicator.show();
33745         
33746         if(this.indicator == this.files.length){
33747             this.nextIndicator.hide();
33748         }
33749         
33750         this.thumbEl.scrollTo('top');
33751         
33752         this.fireEvent('update', this);
33753     },
33754     
33755     onClick : function(e)
33756     {
33757         e.preventDefault();
33758         
33759         this.fireEvent('click', this);
33760     },
33761     
33762     prev : function(e)
33763     {
33764         e.preventDefault();
33765         
33766         this.indicator = Math.max(1, this.indicator - 1);
33767         
33768         this.update();
33769     },
33770     
33771     next : function(e)
33772     {
33773         e.preventDefault();
33774         
33775         this.indicator = Math.min(this.files.length, this.indicator + 1);
33776         
33777         this.update();
33778     }
33779 });
33780 /*
33781  * - LGPL
33782  *
33783  * RadioSet
33784  *
33785  *
33786  */
33787
33788 /**
33789  * @class Roo.bootstrap.RadioSet
33790  * @extends Roo.bootstrap.Input
33791  * Bootstrap RadioSet class
33792  * @cfg {String} indicatorpos (left|right) default left
33793  * @cfg {Boolean} inline (true|false) inline the element (default true)
33794  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33795  * @constructor
33796  * Create a new RadioSet
33797  * @param {Object} config The config object
33798  */
33799
33800 Roo.bootstrap.RadioSet = function(config){
33801     
33802     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33803     
33804     this.radioes = [];
33805     
33806     Roo.bootstrap.RadioSet.register(this);
33807     
33808     this.addEvents({
33809         /**
33810         * @event check
33811         * Fires when the element is checked or unchecked.
33812         * @param {Roo.bootstrap.RadioSet} this This radio
33813         * @param {Roo.bootstrap.Radio} item The checked item
33814         */
33815        check : true,
33816        /**
33817         * @event click
33818         * Fires when the element is click.
33819         * @param {Roo.bootstrap.RadioSet} this This radio set
33820         * @param {Roo.bootstrap.Radio} item The checked item
33821         * @param {Roo.EventObject} e The event object
33822         */
33823        click : true
33824     });
33825     
33826 };
33827
33828 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33829
33830     radioes : false,
33831     
33832     inline : true,
33833     
33834     weight : '',
33835     
33836     indicatorpos : 'left',
33837     
33838     getAutoCreate : function()
33839     {
33840         var label = {
33841             tag : 'label',
33842             cls : 'roo-radio-set-label',
33843             cn : [
33844                 {
33845                     tag : 'span',
33846                     html : this.fieldLabel
33847                 }
33848             ]
33849         };
33850         
33851         if(this.indicatorpos == 'left'){
33852             label.cn.unshift({
33853                 tag : 'i',
33854                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33855                 tooltip : 'This field is required'
33856             });
33857         } else {
33858             label.cn.push({
33859                 tag : 'i',
33860                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33861                 tooltip : 'This field is required'
33862             });
33863         }
33864         
33865         var items = {
33866             tag : 'div',
33867             cls : 'roo-radio-set-items'
33868         };
33869         
33870         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33871         
33872         if (align === 'left' && this.fieldLabel.length) {
33873             
33874             items = {
33875                 cls : "roo-radio-set-right", 
33876                 cn: [
33877                     items
33878                 ]
33879             };
33880             
33881             if(this.labelWidth > 12){
33882                 label.style = "width: " + this.labelWidth + 'px';
33883             }
33884             
33885             if(this.labelWidth < 13 && this.labelmd == 0){
33886                 this.labelmd = this.labelWidth;
33887             }
33888             
33889             if(this.labellg > 0){
33890                 label.cls += ' col-lg-' + this.labellg;
33891                 items.cls += ' col-lg-' + (12 - this.labellg);
33892             }
33893             
33894             if(this.labelmd > 0){
33895                 label.cls += ' col-md-' + this.labelmd;
33896                 items.cls += ' col-md-' + (12 - this.labelmd);
33897             }
33898             
33899             if(this.labelsm > 0){
33900                 label.cls += ' col-sm-' + this.labelsm;
33901                 items.cls += ' col-sm-' + (12 - this.labelsm);
33902             }
33903             
33904             if(this.labelxs > 0){
33905                 label.cls += ' col-xs-' + this.labelxs;
33906                 items.cls += ' col-xs-' + (12 - this.labelxs);
33907             }
33908         }
33909         
33910         var cfg = {
33911             tag : 'div',
33912             cls : 'roo-radio-set',
33913             cn : [
33914                 {
33915                     tag : 'input',
33916                     cls : 'roo-radio-set-input',
33917                     type : 'hidden',
33918                     name : this.name,
33919                     value : this.value ? this.value :  ''
33920                 },
33921                 label,
33922                 items
33923             ]
33924         };
33925         
33926         if(this.weight.length){
33927             cfg.cls += ' roo-radio-' + this.weight;
33928         }
33929         
33930         if(this.inline) {
33931             cfg.cls += ' roo-radio-set-inline';
33932         }
33933         
33934         var settings=this;
33935         ['xs','sm','md','lg'].map(function(size){
33936             if (settings[size]) {
33937                 cfg.cls += ' col-' + size + '-' + settings[size];
33938             }
33939         });
33940         
33941         return cfg;
33942         
33943     },
33944
33945     initEvents : function()
33946     {
33947         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33948         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33949         
33950         if(!this.fieldLabel.length){
33951             this.labelEl.hide();
33952         }
33953         
33954         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33955         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33956         
33957         this.indicator = this.indicatorEl();
33958         
33959         if(this.indicator){
33960             this.indicator.addClass('invisible');
33961         }
33962         
33963         this.originalValue = this.getValue();
33964         
33965     },
33966     
33967     inputEl: function ()
33968     {
33969         return this.el.select('.roo-radio-set-input', true).first();
33970     },
33971     
33972     getChildContainer : function()
33973     {
33974         return this.itemsEl;
33975     },
33976     
33977     register : function(item)
33978     {
33979         this.radioes.push(item);
33980         
33981     },
33982     
33983     validate : function()
33984     {   
33985         if(this.getVisibilityEl().hasClass('hidden')){
33986             return true;
33987         }
33988         
33989         var valid = false;
33990         
33991         Roo.each(this.radioes, function(i){
33992             if(!i.checked){
33993                 return;
33994             }
33995             
33996             valid = true;
33997             return false;
33998         });
33999         
34000         if(this.allowBlank) {
34001             return true;
34002         }
34003         
34004         if(this.disabled || valid){
34005             this.markValid();
34006             return true;
34007         }
34008         
34009         this.markInvalid();
34010         return false;
34011         
34012     },
34013     
34014     markValid : function()
34015     {
34016         if(this.labelEl.isVisible(true)){
34017             this.indicatorEl().removeClass('visible');
34018             this.indicatorEl().addClass('invisible');
34019         }
34020         
34021         this.el.removeClass([this.invalidClass, this.validClass]);
34022         this.el.addClass(this.validClass);
34023         
34024         this.fireEvent('valid', this);
34025     },
34026     
34027     markInvalid : function(msg)
34028     {
34029         if(this.allowBlank || this.disabled){
34030             return;
34031         }
34032         
34033         if(this.labelEl.isVisible(true)){
34034             this.indicatorEl().removeClass('invisible');
34035             this.indicatorEl().addClass('visible');
34036         }
34037         
34038         this.el.removeClass([this.invalidClass, this.validClass]);
34039         this.el.addClass(this.invalidClass);
34040         
34041         this.fireEvent('invalid', this, msg);
34042         
34043     },
34044     
34045     setValue : function(v, suppressEvent)
34046     {   
34047         if(this.value === v){
34048             return;
34049         }
34050         
34051         this.value = v;
34052         
34053         if(this.rendered){
34054             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34055         }
34056         
34057         Roo.each(this.radioes, function(i){
34058             i.checked = false;
34059             i.el.removeClass('checked');
34060         });
34061         
34062         Roo.each(this.radioes, function(i){
34063             
34064             if(i.value === v || i.value.toString() === v.toString()){
34065                 i.checked = true;
34066                 i.el.addClass('checked');
34067                 
34068                 if(suppressEvent !== true){
34069                     this.fireEvent('check', this, i);
34070                 }
34071                 
34072                 return false;
34073             }
34074             
34075         }, this);
34076         
34077         this.validate();
34078     },
34079     
34080     clearInvalid : function(){
34081         
34082         if(!this.el || this.preventMark){
34083             return;
34084         }
34085         
34086         this.el.removeClass([this.invalidClass]);
34087         
34088         this.fireEvent('valid', this);
34089     }
34090     
34091 });
34092
34093 Roo.apply(Roo.bootstrap.RadioSet, {
34094     
34095     groups: {},
34096     
34097     register : function(set)
34098     {
34099         this.groups[set.name] = set;
34100     },
34101     
34102     get: function(name) 
34103     {
34104         if (typeof(this.groups[name]) == 'undefined') {
34105             return false;
34106         }
34107         
34108         return this.groups[name] ;
34109     }
34110     
34111 });
34112 /*
34113  * Based on:
34114  * Ext JS Library 1.1.1
34115  * Copyright(c) 2006-2007, Ext JS, LLC.
34116  *
34117  * Originally Released Under LGPL - original licence link has changed is not relivant.
34118  *
34119  * Fork - LGPL
34120  * <script type="text/javascript">
34121  */
34122
34123
34124 /**
34125  * @class Roo.bootstrap.SplitBar
34126  * @extends Roo.util.Observable
34127  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34128  * <br><br>
34129  * Usage:
34130  * <pre><code>
34131 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34132                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34133 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34134 split.minSize = 100;
34135 split.maxSize = 600;
34136 split.animate = true;
34137 split.on('moved', splitterMoved);
34138 </code></pre>
34139  * @constructor
34140  * Create a new SplitBar
34141  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34142  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34143  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34144  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34145                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34146                         position of the SplitBar).
34147  */
34148 Roo.bootstrap.SplitBar = function(cfg){
34149     
34150     /** @private */
34151     
34152     //{
34153     //  dragElement : elm
34154     //  resizingElement: el,
34155         // optional..
34156     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34157     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34158         // existingProxy ???
34159     //}
34160     
34161     this.el = Roo.get(cfg.dragElement, true);
34162     this.el.dom.unselectable = "on";
34163     /** @private */
34164     this.resizingEl = Roo.get(cfg.resizingElement, true);
34165
34166     /**
34167      * @private
34168      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34169      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34170      * @type Number
34171      */
34172     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34173     
34174     /**
34175      * The minimum size of the resizing element. (Defaults to 0)
34176      * @type Number
34177      */
34178     this.minSize = 0;
34179     
34180     /**
34181      * The maximum size of the resizing element. (Defaults to 2000)
34182      * @type Number
34183      */
34184     this.maxSize = 2000;
34185     
34186     /**
34187      * Whether to animate the transition to the new size
34188      * @type Boolean
34189      */
34190     this.animate = false;
34191     
34192     /**
34193      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34194      * @type Boolean
34195      */
34196     this.useShim = false;
34197     
34198     /** @private */
34199     this.shim = null;
34200     
34201     if(!cfg.existingProxy){
34202         /** @private */
34203         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34204     }else{
34205         this.proxy = Roo.get(cfg.existingProxy).dom;
34206     }
34207     /** @private */
34208     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34209     
34210     /** @private */
34211     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34212     
34213     /** @private */
34214     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34215     
34216     /** @private */
34217     this.dragSpecs = {};
34218     
34219     /**
34220      * @private The adapter to use to positon and resize elements
34221      */
34222     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34223     this.adapter.init(this);
34224     
34225     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34226         /** @private */
34227         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34228         this.el.addClass("roo-splitbar-h");
34229     }else{
34230         /** @private */
34231         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34232         this.el.addClass("roo-splitbar-v");
34233     }
34234     
34235     this.addEvents({
34236         /**
34237          * @event resize
34238          * Fires when the splitter is moved (alias for {@link #event-moved})
34239          * @param {Roo.bootstrap.SplitBar} this
34240          * @param {Number} newSize the new width or height
34241          */
34242         "resize" : true,
34243         /**
34244          * @event moved
34245          * Fires when the splitter is moved
34246          * @param {Roo.bootstrap.SplitBar} this
34247          * @param {Number} newSize the new width or height
34248          */
34249         "moved" : true,
34250         /**
34251          * @event beforeresize
34252          * Fires before the splitter is dragged
34253          * @param {Roo.bootstrap.SplitBar} this
34254          */
34255         "beforeresize" : true,
34256
34257         "beforeapply" : true
34258     });
34259
34260     Roo.util.Observable.call(this);
34261 };
34262
34263 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34264     onStartProxyDrag : function(x, y){
34265         this.fireEvent("beforeresize", this);
34266         if(!this.overlay){
34267             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34268             o.unselectable();
34269             o.enableDisplayMode("block");
34270             // all splitbars share the same overlay
34271             Roo.bootstrap.SplitBar.prototype.overlay = o;
34272         }
34273         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34274         this.overlay.show();
34275         Roo.get(this.proxy).setDisplayed("block");
34276         var size = this.adapter.getElementSize(this);
34277         this.activeMinSize = this.getMinimumSize();;
34278         this.activeMaxSize = this.getMaximumSize();;
34279         var c1 = size - this.activeMinSize;
34280         var c2 = Math.max(this.activeMaxSize - size, 0);
34281         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34282             this.dd.resetConstraints();
34283             this.dd.setXConstraint(
34284                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34285                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34286             );
34287             this.dd.setYConstraint(0, 0);
34288         }else{
34289             this.dd.resetConstraints();
34290             this.dd.setXConstraint(0, 0);
34291             this.dd.setYConstraint(
34292                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34293                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34294             );
34295          }
34296         this.dragSpecs.startSize = size;
34297         this.dragSpecs.startPoint = [x, y];
34298         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34299     },
34300     
34301     /** 
34302      * @private Called after the drag operation by the DDProxy
34303      */
34304     onEndProxyDrag : function(e){
34305         Roo.get(this.proxy).setDisplayed(false);
34306         var endPoint = Roo.lib.Event.getXY(e);
34307         if(this.overlay){
34308             this.overlay.hide();
34309         }
34310         var newSize;
34311         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34312             newSize = this.dragSpecs.startSize + 
34313                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34314                     endPoint[0] - this.dragSpecs.startPoint[0] :
34315                     this.dragSpecs.startPoint[0] - endPoint[0]
34316                 );
34317         }else{
34318             newSize = this.dragSpecs.startSize + 
34319                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34320                     endPoint[1] - this.dragSpecs.startPoint[1] :
34321                     this.dragSpecs.startPoint[1] - endPoint[1]
34322                 );
34323         }
34324         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34325         if(newSize != this.dragSpecs.startSize){
34326             if(this.fireEvent('beforeapply', this, newSize) !== false){
34327                 this.adapter.setElementSize(this, newSize);
34328                 this.fireEvent("moved", this, newSize);
34329                 this.fireEvent("resize", this, newSize);
34330             }
34331         }
34332     },
34333     
34334     /**
34335      * Get the adapter this SplitBar uses
34336      * @return The adapter object
34337      */
34338     getAdapter : function(){
34339         return this.adapter;
34340     },
34341     
34342     /**
34343      * Set the adapter this SplitBar uses
34344      * @param {Object} adapter A SplitBar adapter object
34345      */
34346     setAdapter : function(adapter){
34347         this.adapter = adapter;
34348         this.adapter.init(this);
34349     },
34350     
34351     /**
34352      * Gets the minimum size for the resizing element
34353      * @return {Number} The minimum size
34354      */
34355     getMinimumSize : function(){
34356         return this.minSize;
34357     },
34358     
34359     /**
34360      * Sets the minimum size for the resizing element
34361      * @param {Number} minSize The minimum size
34362      */
34363     setMinimumSize : function(minSize){
34364         this.minSize = minSize;
34365     },
34366     
34367     /**
34368      * Gets the maximum size for the resizing element
34369      * @return {Number} The maximum size
34370      */
34371     getMaximumSize : function(){
34372         return this.maxSize;
34373     },
34374     
34375     /**
34376      * Sets the maximum size for the resizing element
34377      * @param {Number} maxSize The maximum size
34378      */
34379     setMaximumSize : function(maxSize){
34380         this.maxSize = maxSize;
34381     },
34382     
34383     /**
34384      * Sets the initialize size for the resizing element
34385      * @param {Number} size The initial size
34386      */
34387     setCurrentSize : function(size){
34388         var oldAnimate = this.animate;
34389         this.animate = false;
34390         this.adapter.setElementSize(this, size);
34391         this.animate = oldAnimate;
34392     },
34393     
34394     /**
34395      * Destroy this splitbar. 
34396      * @param {Boolean} removeEl True to remove the element
34397      */
34398     destroy : function(removeEl){
34399         if(this.shim){
34400             this.shim.remove();
34401         }
34402         this.dd.unreg();
34403         this.proxy.parentNode.removeChild(this.proxy);
34404         if(removeEl){
34405             this.el.remove();
34406         }
34407     }
34408 });
34409
34410 /**
34411  * @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.
34412  */
34413 Roo.bootstrap.SplitBar.createProxy = function(dir){
34414     var proxy = new Roo.Element(document.createElement("div"));
34415     proxy.unselectable();
34416     var cls = 'roo-splitbar-proxy';
34417     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34418     document.body.appendChild(proxy.dom);
34419     return proxy.dom;
34420 };
34421
34422 /** 
34423  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34424  * Default Adapter. It assumes the splitter and resizing element are not positioned
34425  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34426  */
34427 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34428 };
34429
34430 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34431     // do nothing for now
34432     init : function(s){
34433     
34434     },
34435     /**
34436      * Called before drag operations to get the current size of the resizing element. 
34437      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34438      */
34439      getElementSize : function(s){
34440         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34441             return s.resizingEl.getWidth();
34442         }else{
34443             return s.resizingEl.getHeight();
34444         }
34445     },
34446     
34447     /**
34448      * Called after drag operations to set the size of the resizing element.
34449      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34450      * @param {Number} newSize The new size to set
34451      * @param {Function} onComplete A function to be invoked when resizing is complete
34452      */
34453     setElementSize : function(s, newSize, onComplete){
34454         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34455             if(!s.animate){
34456                 s.resizingEl.setWidth(newSize);
34457                 if(onComplete){
34458                     onComplete(s, newSize);
34459                 }
34460             }else{
34461                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34462             }
34463         }else{
34464             
34465             if(!s.animate){
34466                 s.resizingEl.setHeight(newSize);
34467                 if(onComplete){
34468                     onComplete(s, newSize);
34469                 }
34470             }else{
34471                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34472             }
34473         }
34474     }
34475 };
34476
34477 /** 
34478  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34479  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34480  * Adapter that  moves the splitter element to align with the resized sizing element. 
34481  * Used with an absolute positioned SplitBar.
34482  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34483  * document.body, make sure you assign an id to the body element.
34484  */
34485 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34486     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34487     this.container = Roo.get(container);
34488 };
34489
34490 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34491     init : function(s){
34492         this.basic.init(s);
34493     },
34494     
34495     getElementSize : function(s){
34496         return this.basic.getElementSize(s);
34497     },
34498     
34499     setElementSize : function(s, newSize, onComplete){
34500         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34501     },
34502     
34503     moveSplitter : function(s){
34504         var yes = Roo.bootstrap.SplitBar;
34505         switch(s.placement){
34506             case yes.LEFT:
34507                 s.el.setX(s.resizingEl.getRight());
34508                 break;
34509             case yes.RIGHT:
34510                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34511                 break;
34512             case yes.TOP:
34513                 s.el.setY(s.resizingEl.getBottom());
34514                 break;
34515             case yes.BOTTOM:
34516                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34517                 break;
34518         }
34519     }
34520 };
34521
34522 /**
34523  * Orientation constant - Create a vertical SplitBar
34524  * @static
34525  * @type Number
34526  */
34527 Roo.bootstrap.SplitBar.VERTICAL = 1;
34528
34529 /**
34530  * Orientation constant - Create a horizontal SplitBar
34531  * @static
34532  * @type Number
34533  */
34534 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34535
34536 /**
34537  * Placement constant - The resizing element is to the left of the splitter element
34538  * @static
34539  * @type Number
34540  */
34541 Roo.bootstrap.SplitBar.LEFT = 1;
34542
34543 /**
34544  * Placement constant - The resizing element is to the right of the splitter element
34545  * @static
34546  * @type Number
34547  */
34548 Roo.bootstrap.SplitBar.RIGHT = 2;
34549
34550 /**
34551  * Placement constant - The resizing element is positioned above the splitter element
34552  * @static
34553  * @type Number
34554  */
34555 Roo.bootstrap.SplitBar.TOP = 3;
34556
34557 /**
34558  * Placement constant - The resizing element is positioned under splitter element
34559  * @static
34560  * @type Number
34561  */
34562 Roo.bootstrap.SplitBar.BOTTOM = 4;
34563 Roo.namespace("Roo.bootstrap.layout");/*
34564  * Based on:
34565  * Ext JS Library 1.1.1
34566  * Copyright(c) 2006-2007, Ext JS, LLC.
34567  *
34568  * Originally Released Under LGPL - original licence link has changed is not relivant.
34569  *
34570  * Fork - LGPL
34571  * <script type="text/javascript">
34572  */
34573
34574 /**
34575  * @class Roo.bootstrap.layout.Manager
34576  * @extends Roo.bootstrap.Component
34577  * Base class for layout managers.
34578  */
34579 Roo.bootstrap.layout.Manager = function(config)
34580 {
34581     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34582
34583
34584
34585
34586
34587     /** false to disable window resize monitoring @type Boolean */
34588     this.monitorWindowResize = true;
34589     this.regions = {};
34590     this.addEvents({
34591         /**
34592          * @event layout
34593          * Fires when a layout is performed.
34594          * @param {Roo.LayoutManager} this
34595          */
34596         "layout" : true,
34597         /**
34598          * @event regionresized
34599          * Fires when the user resizes a region.
34600          * @param {Roo.LayoutRegion} region The resized region
34601          * @param {Number} newSize The new size (width for east/west, height for north/south)
34602          */
34603         "regionresized" : true,
34604         /**
34605          * @event regioncollapsed
34606          * Fires when a region is collapsed.
34607          * @param {Roo.LayoutRegion} region The collapsed region
34608          */
34609         "regioncollapsed" : true,
34610         /**
34611          * @event regionexpanded
34612          * Fires when a region is expanded.
34613          * @param {Roo.LayoutRegion} region The expanded region
34614          */
34615         "regionexpanded" : true
34616     });
34617     this.updating = false;
34618
34619     if (config.el) {
34620         this.el = Roo.get(config.el);
34621         this.initEvents();
34622     }
34623
34624 };
34625
34626 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34627
34628
34629     regions : null,
34630
34631     monitorWindowResize : true,
34632
34633
34634     updating : false,
34635
34636
34637     onRender : function(ct, position)
34638     {
34639         if(!this.el){
34640             this.el = Roo.get(ct);
34641             this.initEvents();
34642         }
34643         //this.fireEvent('render',this);
34644     },
34645
34646
34647     initEvents: function()
34648     {
34649
34650
34651         // ie scrollbar fix
34652         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34653             document.body.scroll = "no";
34654         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34655             this.el.position('relative');
34656         }
34657         this.id = this.el.id;
34658         this.el.addClass("roo-layout-container");
34659         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34660         if(this.el.dom != document.body ) {
34661             this.el.on('resize', this.layout,this);
34662             this.el.on('show', this.layout,this);
34663         }
34664
34665     },
34666
34667     /**
34668      * Returns true if this layout is currently being updated
34669      * @return {Boolean}
34670      */
34671     isUpdating : function(){
34672         return this.updating;
34673     },
34674
34675     /**
34676      * Suspend the LayoutManager from doing auto-layouts while
34677      * making multiple add or remove calls
34678      */
34679     beginUpdate : function(){
34680         this.updating = true;
34681     },
34682
34683     /**
34684      * Restore auto-layouts and optionally disable the manager from performing a layout
34685      * @param {Boolean} noLayout true to disable a layout update
34686      */
34687     endUpdate : function(noLayout){
34688         this.updating = false;
34689         if(!noLayout){
34690             this.layout();
34691         }
34692     },
34693
34694     layout: function(){
34695         // abstract...
34696     },
34697
34698     onRegionResized : function(region, newSize){
34699         this.fireEvent("regionresized", region, newSize);
34700         this.layout();
34701     },
34702
34703     onRegionCollapsed : function(region){
34704         this.fireEvent("regioncollapsed", region);
34705     },
34706
34707     onRegionExpanded : function(region){
34708         this.fireEvent("regionexpanded", region);
34709     },
34710
34711     /**
34712      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34713      * performs box-model adjustments.
34714      * @return {Object} The size as an object {width: (the width), height: (the height)}
34715      */
34716     getViewSize : function()
34717     {
34718         var size;
34719         if(this.el.dom != document.body){
34720             size = this.el.getSize();
34721         }else{
34722             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34723         }
34724         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34725         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34726         return size;
34727     },
34728
34729     /**
34730      * Returns the Element this layout is bound to.
34731      * @return {Roo.Element}
34732      */
34733     getEl : function(){
34734         return this.el;
34735     },
34736
34737     /**
34738      * Returns the specified region.
34739      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34740      * @return {Roo.LayoutRegion}
34741      */
34742     getRegion : function(target){
34743         return this.regions[target.toLowerCase()];
34744     },
34745
34746     onWindowResize : function(){
34747         if(this.monitorWindowResize){
34748             this.layout();
34749         }
34750     }
34751 });
34752 /*
34753  * Based on:
34754  * Ext JS Library 1.1.1
34755  * Copyright(c) 2006-2007, Ext JS, LLC.
34756  *
34757  * Originally Released Under LGPL - original licence link has changed is not relivant.
34758  *
34759  * Fork - LGPL
34760  * <script type="text/javascript">
34761  */
34762 /**
34763  * @class Roo.bootstrap.layout.Border
34764  * @extends Roo.bootstrap.layout.Manager
34765  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34766  * please see: examples/bootstrap/nested.html<br><br>
34767  
34768 <b>The container the layout is rendered into can be either the body element or any other element.
34769 If it is not the body element, the container needs to either be an absolute positioned element,
34770 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34771 the container size if it is not the body element.</b>
34772
34773 * @constructor
34774 * Create a new Border
34775 * @param {Object} config Configuration options
34776  */
34777 Roo.bootstrap.layout.Border = function(config){
34778     config = config || {};
34779     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34780     
34781     
34782     
34783     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34784         if(config[region]){
34785             config[region].region = region;
34786             this.addRegion(config[region]);
34787         }
34788     },this);
34789     
34790 };
34791
34792 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34793
34794 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34795     /**
34796      * Creates and adds a new region if it doesn't already exist.
34797      * @param {String} target The target region key (north, south, east, west or center).
34798      * @param {Object} config The regions config object
34799      * @return {BorderLayoutRegion} The new region
34800      */
34801     addRegion : function(config)
34802     {
34803         if(!this.regions[config.region]){
34804             var r = this.factory(config);
34805             this.bindRegion(r);
34806         }
34807         return this.regions[config.region];
34808     },
34809
34810     // private (kinda)
34811     bindRegion : function(r){
34812         this.regions[r.config.region] = r;
34813         
34814         r.on("visibilitychange",    this.layout, this);
34815         r.on("paneladded",          this.layout, this);
34816         r.on("panelremoved",        this.layout, this);
34817         r.on("invalidated",         this.layout, this);
34818         r.on("resized",             this.onRegionResized, this);
34819         r.on("collapsed",           this.onRegionCollapsed, this);
34820         r.on("expanded",            this.onRegionExpanded, this);
34821     },
34822
34823     /**
34824      * Performs a layout update.
34825      */
34826     layout : function()
34827     {
34828         if(this.updating) {
34829             return;
34830         }
34831         
34832         // render all the rebions if they have not been done alreayd?
34833         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34834             if(this.regions[region] && !this.regions[region].bodyEl){
34835                 this.regions[region].onRender(this.el)
34836             }
34837         },this);
34838         
34839         var size = this.getViewSize();
34840         var w = size.width;
34841         var h = size.height;
34842         var centerW = w;
34843         var centerH = h;
34844         var centerY = 0;
34845         var centerX = 0;
34846         //var x = 0, y = 0;
34847
34848         var rs = this.regions;
34849         var north = rs["north"];
34850         var south = rs["south"]; 
34851         var west = rs["west"];
34852         var east = rs["east"];
34853         var center = rs["center"];
34854         //if(this.hideOnLayout){ // not supported anymore
34855             //c.el.setStyle("display", "none");
34856         //}
34857         if(north && north.isVisible()){
34858             var b = north.getBox();
34859             var m = north.getMargins();
34860             b.width = w - (m.left+m.right);
34861             b.x = m.left;
34862             b.y = m.top;
34863             centerY = b.height + b.y + m.bottom;
34864             centerH -= centerY;
34865             north.updateBox(this.safeBox(b));
34866         }
34867         if(south && south.isVisible()){
34868             var b = south.getBox();
34869             var m = south.getMargins();
34870             b.width = w - (m.left+m.right);
34871             b.x = m.left;
34872             var totalHeight = (b.height + m.top + m.bottom);
34873             b.y = h - totalHeight + m.top;
34874             centerH -= totalHeight;
34875             south.updateBox(this.safeBox(b));
34876         }
34877         if(west && west.isVisible()){
34878             var b = west.getBox();
34879             var m = west.getMargins();
34880             b.height = centerH - (m.top+m.bottom);
34881             b.x = m.left;
34882             b.y = centerY + m.top;
34883             var totalWidth = (b.width + m.left + m.right);
34884             centerX += totalWidth;
34885             centerW -= totalWidth;
34886             west.updateBox(this.safeBox(b));
34887         }
34888         if(east && east.isVisible()){
34889             var b = east.getBox();
34890             var m = east.getMargins();
34891             b.height = centerH - (m.top+m.bottom);
34892             var totalWidth = (b.width + m.left + m.right);
34893             b.x = w - totalWidth + m.left;
34894             b.y = centerY + m.top;
34895             centerW -= totalWidth;
34896             east.updateBox(this.safeBox(b));
34897         }
34898         if(center){
34899             var m = center.getMargins();
34900             var centerBox = {
34901                 x: centerX + m.left,
34902                 y: centerY + m.top,
34903                 width: centerW - (m.left+m.right),
34904                 height: centerH - (m.top+m.bottom)
34905             };
34906             //if(this.hideOnLayout){
34907                 //center.el.setStyle("display", "block");
34908             //}
34909             center.updateBox(this.safeBox(centerBox));
34910         }
34911         this.el.repaint();
34912         this.fireEvent("layout", this);
34913     },
34914
34915     // private
34916     safeBox : function(box){
34917         box.width = Math.max(0, box.width);
34918         box.height = Math.max(0, box.height);
34919         return box;
34920     },
34921
34922     /**
34923      * Adds a ContentPanel (or subclass) to this layout.
34924      * @param {String} target The target region key (north, south, east, west or center).
34925      * @param {Roo.ContentPanel} panel The panel to add
34926      * @return {Roo.ContentPanel} The added panel
34927      */
34928     add : function(target, panel){
34929          
34930         target = target.toLowerCase();
34931         return this.regions[target].add(panel);
34932     },
34933
34934     /**
34935      * Remove a ContentPanel (or subclass) to this layout.
34936      * @param {String} target The target region key (north, south, east, west or center).
34937      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34938      * @return {Roo.ContentPanel} The removed panel
34939      */
34940     remove : function(target, panel){
34941         target = target.toLowerCase();
34942         return this.regions[target].remove(panel);
34943     },
34944
34945     /**
34946      * Searches all regions for a panel with the specified id
34947      * @param {String} panelId
34948      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34949      */
34950     findPanel : function(panelId){
34951         var rs = this.regions;
34952         for(var target in rs){
34953             if(typeof rs[target] != "function"){
34954                 var p = rs[target].getPanel(panelId);
34955                 if(p){
34956                     return p;
34957                 }
34958             }
34959         }
34960         return null;
34961     },
34962
34963     /**
34964      * Searches all regions for a panel with the specified id and activates (shows) it.
34965      * @param {String/ContentPanel} panelId The panels id or the panel itself
34966      * @return {Roo.ContentPanel} The shown panel or null
34967      */
34968     showPanel : function(panelId) {
34969       var rs = this.regions;
34970       for(var target in rs){
34971          var r = rs[target];
34972          if(typeof r != "function"){
34973             if(r.hasPanel(panelId)){
34974                return r.showPanel(panelId);
34975             }
34976          }
34977       }
34978       return null;
34979    },
34980
34981    /**
34982      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34983      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34984      */
34985    /*
34986     restoreState : function(provider){
34987         if(!provider){
34988             provider = Roo.state.Manager;
34989         }
34990         var sm = new Roo.LayoutStateManager();
34991         sm.init(this, provider);
34992     },
34993 */
34994  
34995  
34996     /**
34997      * Adds a xtype elements to the layout.
34998      * <pre><code>
34999
35000 layout.addxtype({
35001        xtype : 'ContentPanel',
35002        region: 'west',
35003        items: [ .... ]
35004    }
35005 );
35006
35007 layout.addxtype({
35008         xtype : 'NestedLayoutPanel',
35009         region: 'west',
35010         layout: {
35011            center: { },
35012            west: { }   
35013         },
35014         items : [ ... list of content panels or nested layout panels.. ]
35015    }
35016 );
35017 </code></pre>
35018      * @param {Object} cfg Xtype definition of item to add.
35019      */
35020     addxtype : function(cfg)
35021     {
35022         // basically accepts a pannel...
35023         // can accept a layout region..!?!?
35024         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35025         
35026         
35027         // theory?  children can only be panels??
35028         
35029         //if (!cfg.xtype.match(/Panel$/)) {
35030         //    return false;
35031         //}
35032         var ret = false;
35033         
35034         if (typeof(cfg.region) == 'undefined') {
35035             Roo.log("Failed to add Panel, region was not set");
35036             Roo.log(cfg);
35037             return false;
35038         }
35039         var region = cfg.region;
35040         delete cfg.region;
35041         
35042           
35043         var xitems = [];
35044         if (cfg.items) {
35045             xitems = cfg.items;
35046             delete cfg.items;
35047         }
35048         var nb = false;
35049         
35050         switch(cfg.xtype) 
35051         {
35052             case 'Content':  // ContentPanel (el, cfg)
35053             case 'Scroll':  // ContentPanel (el, cfg)
35054             case 'View': 
35055                 cfg.autoCreate = true;
35056                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35057                 //} else {
35058                 //    var el = this.el.createChild();
35059                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35060                 //}
35061                 
35062                 this.add(region, ret);
35063                 break;
35064             
35065             /*
35066             case 'TreePanel': // our new panel!
35067                 cfg.el = this.el.createChild();
35068                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35069                 this.add(region, ret);
35070                 break;
35071             */
35072             
35073             case 'Nest': 
35074                 // create a new Layout (which is  a Border Layout...
35075                 
35076                 var clayout = cfg.layout;
35077                 clayout.el  = this.el.createChild();
35078                 clayout.items   = clayout.items  || [];
35079                 
35080                 delete cfg.layout;
35081                 
35082                 // replace this exitems with the clayout ones..
35083                 xitems = clayout.items;
35084                  
35085                 // force background off if it's in center...
35086                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35087                     cfg.background = false;
35088                 }
35089                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35090                 
35091                 
35092                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35093                 //console.log('adding nested layout panel '  + cfg.toSource());
35094                 this.add(region, ret);
35095                 nb = {}; /// find first...
35096                 break;
35097             
35098             case 'Grid':
35099                 
35100                 // needs grid and region
35101                 
35102                 //var el = this.getRegion(region).el.createChild();
35103                 /*
35104                  *var el = this.el.createChild();
35105                 // create the grid first...
35106                 cfg.grid.container = el;
35107                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35108                 */
35109                 
35110                 if (region == 'center' && this.active ) {
35111                     cfg.background = false;
35112                 }
35113                 
35114                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35115                 
35116                 this.add(region, ret);
35117                 /*
35118                 if (cfg.background) {
35119                     // render grid on panel activation (if panel background)
35120                     ret.on('activate', function(gp) {
35121                         if (!gp.grid.rendered) {
35122                     //        gp.grid.render(el);
35123                         }
35124                     });
35125                 } else {
35126                   //  cfg.grid.render(el);
35127                 }
35128                 */
35129                 break;
35130            
35131            
35132             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35133                 // it was the old xcomponent building that caused this before.
35134                 // espeically if border is the top element in the tree.
35135                 ret = this;
35136                 break; 
35137                 
35138                     
35139                 
35140                 
35141                 
35142             default:
35143                 /*
35144                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35145                     
35146                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35147                     this.add(region, ret);
35148                 } else {
35149                 */
35150                     Roo.log(cfg);
35151                     throw "Can not add '" + cfg.xtype + "' to Border";
35152                     return null;
35153              
35154                                 
35155              
35156         }
35157         this.beginUpdate();
35158         // add children..
35159         var region = '';
35160         var abn = {};
35161         Roo.each(xitems, function(i)  {
35162             region = nb && i.region ? i.region : false;
35163             
35164             var add = ret.addxtype(i);
35165            
35166             if (region) {
35167                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35168                 if (!i.background) {
35169                     abn[region] = nb[region] ;
35170                 }
35171             }
35172             
35173         });
35174         this.endUpdate();
35175
35176         // make the last non-background panel active..
35177         //if (nb) { Roo.log(abn); }
35178         if (nb) {
35179             
35180             for(var r in abn) {
35181                 region = this.getRegion(r);
35182                 if (region) {
35183                     // tried using nb[r], but it does not work..
35184                      
35185                     region.showPanel(abn[r]);
35186                    
35187                 }
35188             }
35189         }
35190         return ret;
35191         
35192     },
35193     
35194     
35195 // private
35196     factory : function(cfg)
35197     {
35198         
35199         var validRegions = Roo.bootstrap.layout.Border.regions;
35200
35201         var target = cfg.region;
35202         cfg.mgr = this;
35203         
35204         var r = Roo.bootstrap.layout;
35205         Roo.log(target);
35206         switch(target){
35207             case "north":
35208                 return new r.North(cfg);
35209             case "south":
35210                 return new r.South(cfg);
35211             case "east":
35212                 return new r.East(cfg);
35213             case "west":
35214                 return new r.West(cfg);
35215             case "center":
35216                 return new r.Center(cfg);
35217         }
35218         throw 'Layout region "'+target+'" not supported.';
35219     }
35220     
35221     
35222 });
35223  /*
35224  * Based on:
35225  * Ext JS Library 1.1.1
35226  * Copyright(c) 2006-2007, Ext JS, LLC.
35227  *
35228  * Originally Released Under LGPL - original licence link has changed is not relivant.
35229  *
35230  * Fork - LGPL
35231  * <script type="text/javascript">
35232  */
35233  
35234 /**
35235  * @class Roo.bootstrap.layout.Basic
35236  * @extends Roo.util.Observable
35237  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35238  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35239  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35240  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35241  * @cfg {string}   region  the region that it inhabits..
35242  * @cfg {bool}   skipConfig skip config?
35243  * 
35244
35245  */
35246 Roo.bootstrap.layout.Basic = function(config){
35247     
35248     this.mgr = config.mgr;
35249     
35250     this.position = config.region;
35251     
35252     var skipConfig = config.skipConfig;
35253     
35254     this.events = {
35255         /**
35256          * @scope Roo.BasicLayoutRegion
35257          */
35258         
35259         /**
35260          * @event beforeremove
35261          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35262          * @param {Roo.LayoutRegion} this
35263          * @param {Roo.ContentPanel} panel The panel
35264          * @param {Object} e The cancel event object
35265          */
35266         "beforeremove" : true,
35267         /**
35268          * @event invalidated
35269          * Fires when the layout for this region is changed.
35270          * @param {Roo.LayoutRegion} this
35271          */
35272         "invalidated" : true,
35273         /**
35274          * @event visibilitychange
35275          * Fires when this region is shown or hidden 
35276          * @param {Roo.LayoutRegion} this
35277          * @param {Boolean} visibility true or false
35278          */
35279         "visibilitychange" : true,
35280         /**
35281          * @event paneladded
35282          * Fires when a panel is added. 
35283          * @param {Roo.LayoutRegion} this
35284          * @param {Roo.ContentPanel} panel The panel
35285          */
35286         "paneladded" : true,
35287         /**
35288          * @event panelremoved
35289          * Fires when a panel is removed. 
35290          * @param {Roo.LayoutRegion} this
35291          * @param {Roo.ContentPanel} panel The panel
35292          */
35293         "panelremoved" : true,
35294         /**
35295          * @event beforecollapse
35296          * Fires when this region before collapse.
35297          * @param {Roo.LayoutRegion} this
35298          */
35299         "beforecollapse" : true,
35300         /**
35301          * @event collapsed
35302          * Fires when this region is collapsed.
35303          * @param {Roo.LayoutRegion} this
35304          */
35305         "collapsed" : true,
35306         /**
35307          * @event expanded
35308          * Fires when this region is expanded.
35309          * @param {Roo.LayoutRegion} this
35310          */
35311         "expanded" : true,
35312         /**
35313          * @event slideshow
35314          * Fires when this region is slid into view.
35315          * @param {Roo.LayoutRegion} this
35316          */
35317         "slideshow" : true,
35318         /**
35319          * @event slidehide
35320          * Fires when this region slides out of view. 
35321          * @param {Roo.LayoutRegion} this
35322          */
35323         "slidehide" : true,
35324         /**
35325          * @event panelactivated
35326          * Fires when a panel is activated. 
35327          * @param {Roo.LayoutRegion} this
35328          * @param {Roo.ContentPanel} panel The activated panel
35329          */
35330         "panelactivated" : true,
35331         /**
35332          * @event resized
35333          * Fires when the user resizes this region. 
35334          * @param {Roo.LayoutRegion} this
35335          * @param {Number} newSize The new size (width for east/west, height for north/south)
35336          */
35337         "resized" : true
35338     };
35339     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35340     this.panels = new Roo.util.MixedCollection();
35341     this.panels.getKey = this.getPanelId.createDelegate(this);
35342     this.box = null;
35343     this.activePanel = null;
35344     // ensure listeners are added...
35345     
35346     if (config.listeners || config.events) {
35347         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35348             listeners : config.listeners || {},
35349             events : config.events || {}
35350         });
35351     }
35352     
35353     if(skipConfig !== true){
35354         this.applyConfig(config);
35355     }
35356 };
35357
35358 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35359 {
35360     getPanelId : function(p){
35361         return p.getId();
35362     },
35363     
35364     applyConfig : function(config){
35365         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35366         this.config = config;
35367         
35368     },
35369     
35370     /**
35371      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35372      * the width, for horizontal (north, south) the height.
35373      * @param {Number} newSize The new width or height
35374      */
35375     resizeTo : function(newSize){
35376         var el = this.el ? this.el :
35377                  (this.activePanel ? this.activePanel.getEl() : null);
35378         if(el){
35379             switch(this.position){
35380                 case "east":
35381                 case "west":
35382                     el.setWidth(newSize);
35383                     this.fireEvent("resized", this, newSize);
35384                 break;
35385                 case "north":
35386                 case "south":
35387                     el.setHeight(newSize);
35388                     this.fireEvent("resized", this, newSize);
35389                 break;                
35390             }
35391         }
35392     },
35393     
35394     getBox : function(){
35395         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35396     },
35397     
35398     getMargins : function(){
35399         return this.margins;
35400     },
35401     
35402     updateBox : function(box){
35403         this.box = box;
35404         var el = this.activePanel.getEl();
35405         el.dom.style.left = box.x + "px";
35406         el.dom.style.top = box.y + "px";
35407         this.activePanel.setSize(box.width, box.height);
35408     },
35409     
35410     /**
35411      * Returns the container element for this region.
35412      * @return {Roo.Element}
35413      */
35414     getEl : function(){
35415         return this.activePanel;
35416     },
35417     
35418     /**
35419      * Returns true if this region is currently visible.
35420      * @return {Boolean}
35421      */
35422     isVisible : function(){
35423         return this.activePanel ? true : false;
35424     },
35425     
35426     setActivePanel : function(panel){
35427         panel = this.getPanel(panel);
35428         if(this.activePanel && this.activePanel != panel){
35429             this.activePanel.setActiveState(false);
35430             this.activePanel.getEl().setLeftTop(-10000,-10000);
35431         }
35432         this.activePanel = panel;
35433         panel.setActiveState(true);
35434         if(this.box){
35435             panel.setSize(this.box.width, this.box.height);
35436         }
35437         this.fireEvent("panelactivated", this, panel);
35438         this.fireEvent("invalidated");
35439     },
35440     
35441     /**
35442      * Show the specified panel.
35443      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35444      * @return {Roo.ContentPanel} The shown panel or null
35445      */
35446     showPanel : function(panel){
35447         panel = this.getPanel(panel);
35448         if(panel){
35449             this.setActivePanel(panel);
35450         }
35451         return panel;
35452     },
35453     
35454     /**
35455      * Get the active panel for this region.
35456      * @return {Roo.ContentPanel} The active panel or null
35457      */
35458     getActivePanel : function(){
35459         return this.activePanel;
35460     },
35461     
35462     /**
35463      * Add the passed ContentPanel(s)
35464      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35465      * @return {Roo.ContentPanel} The panel added (if only one was added)
35466      */
35467     add : function(panel){
35468         if(arguments.length > 1){
35469             for(var i = 0, len = arguments.length; i < len; i++) {
35470                 this.add(arguments[i]);
35471             }
35472             return null;
35473         }
35474         if(this.hasPanel(panel)){
35475             this.showPanel(panel);
35476             return panel;
35477         }
35478         var el = panel.getEl();
35479         if(el.dom.parentNode != this.mgr.el.dom){
35480             this.mgr.el.dom.appendChild(el.dom);
35481         }
35482         if(panel.setRegion){
35483             panel.setRegion(this);
35484         }
35485         this.panels.add(panel);
35486         el.setStyle("position", "absolute");
35487         if(!panel.background){
35488             this.setActivePanel(panel);
35489             if(this.config.initialSize && this.panels.getCount()==1){
35490                 this.resizeTo(this.config.initialSize);
35491             }
35492         }
35493         this.fireEvent("paneladded", this, panel);
35494         return panel;
35495     },
35496     
35497     /**
35498      * Returns true if the panel is in this region.
35499      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35500      * @return {Boolean}
35501      */
35502     hasPanel : function(panel){
35503         if(typeof panel == "object"){ // must be panel obj
35504             panel = panel.getId();
35505         }
35506         return this.getPanel(panel) ? true : false;
35507     },
35508     
35509     /**
35510      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35511      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35512      * @param {Boolean} preservePanel Overrides the config preservePanel option
35513      * @return {Roo.ContentPanel} The panel that was removed
35514      */
35515     remove : function(panel, preservePanel){
35516         panel = this.getPanel(panel);
35517         if(!panel){
35518             return null;
35519         }
35520         var e = {};
35521         this.fireEvent("beforeremove", this, panel, e);
35522         if(e.cancel === true){
35523             return null;
35524         }
35525         var panelId = panel.getId();
35526         this.panels.removeKey(panelId);
35527         return panel;
35528     },
35529     
35530     /**
35531      * Returns the panel specified or null if it's not in this region.
35532      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35533      * @return {Roo.ContentPanel}
35534      */
35535     getPanel : function(id){
35536         if(typeof id == "object"){ // must be panel obj
35537             return id;
35538         }
35539         return this.panels.get(id);
35540     },
35541     
35542     /**
35543      * Returns this regions position (north/south/east/west/center).
35544      * @return {String} 
35545      */
35546     getPosition: function(){
35547         return this.position;    
35548     }
35549 });/*
35550  * Based on:
35551  * Ext JS Library 1.1.1
35552  * Copyright(c) 2006-2007, Ext JS, LLC.
35553  *
35554  * Originally Released Under LGPL - original licence link has changed is not relivant.
35555  *
35556  * Fork - LGPL
35557  * <script type="text/javascript">
35558  */
35559  
35560 /**
35561  * @class Roo.bootstrap.layout.Region
35562  * @extends Roo.bootstrap.layout.Basic
35563  * This class represents a region in a layout manager.
35564  
35565  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35566  * @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})
35567  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35568  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35569  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35570  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35571  * @cfg {String}    title           The title for the region (overrides panel titles)
35572  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35573  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35574  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35575  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35576  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35577  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35578  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35579  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35580  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35581  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35582
35583  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35584  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35585  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35586  * @cfg {Number}    width           For East/West panels
35587  * @cfg {Number}    height          For North/South panels
35588  * @cfg {Boolean}   split           To show the splitter
35589  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35590  * 
35591  * @cfg {string}   cls             Extra CSS classes to add to region
35592  * 
35593  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35594  * @cfg {string}   region  the region that it inhabits..
35595  *
35596
35597  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35598  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35599
35600  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35601  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35602  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35603  */
35604 Roo.bootstrap.layout.Region = function(config)
35605 {
35606     this.applyConfig(config);
35607
35608     var mgr = config.mgr;
35609     var pos = config.region;
35610     config.skipConfig = true;
35611     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35612     
35613     if (mgr.el) {
35614         this.onRender(mgr.el);   
35615     }
35616      
35617     this.visible = true;
35618     this.collapsed = false;
35619     this.unrendered_panels = [];
35620 };
35621
35622 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35623
35624     position: '', // set by wrapper (eg. north/south etc..)
35625     unrendered_panels : null,  // unrendered panels.
35626     createBody : function(){
35627         /** This region's body element 
35628         * @type Roo.Element */
35629         this.bodyEl = this.el.createChild({
35630                 tag: "div",
35631                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35632         });
35633     },
35634
35635     onRender: function(ctr, pos)
35636     {
35637         var dh = Roo.DomHelper;
35638         /** This region's container element 
35639         * @type Roo.Element */
35640         this.el = dh.append(ctr.dom, {
35641                 tag: "div",
35642                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35643             }, true);
35644         /** This region's title element 
35645         * @type Roo.Element */
35646     
35647         this.titleEl = dh.append(this.el.dom,
35648             {
35649                     tag: "div",
35650                     unselectable: "on",
35651                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35652                     children:[
35653                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35654                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35655                     ]}, true);
35656         
35657         this.titleEl.enableDisplayMode();
35658         /** This region's title text element 
35659         * @type HTMLElement */
35660         this.titleTextEl = this.titleEl.dom.firstChild;
35661         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35662         /*
35663         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35664         this.closeBtn.enableDisplayMode();
35665         this.closeBtn.on("click", this.closeClicked, this);
35666         this.closeBtn.hide();
35667     */
35668         this.createBody(this.config);
35669         if(this.config.hideWhenEmpty){
35670             this.hide();
35671             this.on("paneladded", this.validateVisibility, this);
35672             this.on("panelremoved", this.validateVisibility, this);
35673         }
35674         if(this.autoScroll){
35675             this.bodyEl.setStyle("overflow", "auto");
35676         }else{
35677             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35678         }
35679         //if(c.titlebar !== false){
35680             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35681                 this.titleEl.hide();
35682             }else{
35683                 this.titleEl.show();
35684                 if(this.config.title){
35685                     this.titleTextEl.innerHTML = this.config.title;
35686                 }
35687             }
35688         //}
35689         if(this.config.collapsed){
35690             this.collapse(true);
35691         }
35692         if(this.config.hidden){
35693             this.hide();
35694         }
35695         
35696         if (this.unrendered_panels && this.unrendered_panels.length) {
35697             for (var i =0;i< this.unrendered_panels.length; i++) {
35698                 this.add(this.unrendered_panels[i]);
35699             }
35700             this.unrendered_panels = null;
35701             
35702         }
35703         
35704     },
35705     
35706     applyConfig : function(c)
35707     {
35708         /*
35709          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35710             var dh = Roo.DomHelper;
35711             if(c.titlebar !== false){
35712                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35713                 this.collapseBtn.on("click", this.collapse, this);
35714                 this.collapseBtn.enableDisplayMode();
35715                 /*
35716                 if(c.showPin === true || this.showPin){
35717                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35718                     this.stickBtn.enableDisplayMode();
35719                     this.stickBtn.on("click", this.expand, this);
35720                     this.stickBtn.hide();
35721                 }
35722                 
35723             }
35724             */
35725             /** This region's collapsed element
35726             * @type Roo.Element */
35727             /*
35728              *
35729             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35730                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35731             ]}, true);
35732             
35733             if(c.floatable !== false){
35734                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35735                this.collapsedEl.on("click", this.collapseClick, this);
35736             }
35737
35738             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35739                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35740                    id: "message", unselectable: "on", style:{"float":"left"}});
35741                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35742              }
35743             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35744             this.expandBtn.on("click", this.expand, this);
35745             
35746         }
35747         
35748         if(this.collapseBtn){
35749             this.collapseBtn.setVisible(c.collapsible == true);
35750         }
35751         
35752         this.cmargins = c.cmargins || this.cmargins ||
35753                          (this.position == "west" || this.position == "east" ?
35754                              {top: 0, left: 2, right:2, bottom: 0} :
35755                              {top: 2, left: 0, right:0, bottom: 2});
35756         */
35757         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35758         
35759         
35760         this.bottomTabs = c.tabPosition != "top";
35761         
35762         this.autoScroll = c.autoScroll || false;
35763         
35764         
35765        
35766         
35767         this.duration = c.duration || .30;
35768         this.slideDuration = c.slideDuration || .45;
35769         this.config = c;
35770        
35771     },
35772     /**
35773      * Returns true if this region is currently visible.
35774      * @return {Boolean}
35775      */
35776     isVisible : function(){
35777         return this.visible;
35778     },
35779
35780     /**
35781      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35782      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35783      */
35784     //setCollapsedTitle : function(title){
35785     //    title = title || "&#160;";
35786      //   if(this.collapsedTitleTextEl){
35787       //      this.collapsedTitleTextEl.innerHTML = title;
35788        // }
35789     //},
35790
35791     getBox : function(){
35792         var b;
35793       //  if(!this.collapsed){
35794             b = this.el.getBox(false, true);
35795        // }else{
35796           //  b = this.collapsedEl.getBox(false, true);
35797         //}
35798         return b;
35799     },
35800
35801     getMargins : function(){
35802         return this.margins;
35803         //return this.collapsed ? this.cmargins : this.margins;
35804     },
35805 /*
35806     highlight : function(){
35807         this.el.addClass("x-layout-panel-dragover");
35808     },
35809
35810     unhighlight : function(){
35811         this.el.removeClass("x-layout-panel-dragover");
35812     },
35813 */
35814     updateBox : function(box)
35815     {
35816         if (!this.bodyEl) {
35817             return; // not rendered yet..
35818         }
35819         
35820         this.box = box;
35821         if(!this.collapsed){
35822             this.el.dom.style.left = box.x + "px";
35823             this.el.dom.style.top = box.y + "px";
35824             this.updateBody(box.width, box.height);
35825         }else{
35826             this.collapsedEl.dom.style.left = box.x + "px";
35827             this.collapsedEl.dom.style.top = box.y + "px";
35828             this.collapsedEl.setSize(box.width, box.height);
35829         }
35830         if(this.tabs){
35831             this.tabs.autoSizeTabs();
35832         }
35833     },
35834
35835     updateBody : function(w, h)
35836     {
35837         if(w !== null){
35838             this.el.setWidth(w);
35839             w -= this.el.getBorderWidth("rl");
35840             if(this.config.adjustments){
35841                 w += this.config.adjustments[0];
35842             }
35843         }
35844         if(h !== null && h > 0){
35845             this.el.setHeight(h);
35846             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35847             h -= this.el.getBorderWidth("tb");
35848             if(this.config.adjustments){
35849                 h += this.config.adjustments[1];
35850             }
35851             this.bodyEl.setHeight(h);
35852             if(this.tabs){
35853                 h = this.tabs.syncHeight(h);
35854             }
35855         }
35856         if(this.panelSize){
35857             w = w !== null ? w : this.panelSize.width;
35858             h = h !== null ? h : this.panelSize.height;
35859         }
35860         if(this.activePanel){
35861             var el = this.activePanel.getEl();
35862             w = w !== null ? w : el.getWidth();
35863             h = h !== null ? h : el.getHeight();
35864             this.panelSize = {width: w, height: h};
35865             this.activePanel.setSize(w, h);
35866         }
35867         if(Roo.isIE && this.tabs){
35868             this.tabs.el.repaint();
35869         }
35870     },
35871
35872     /**
35873      * Returns the container element for this region.
35874      * @return {Roo.Element}
35875      */
35876     getEl : function(){
35877         return this.el;
35878     },
35879
35880     /**
35881      * Hides this region.
35882      */
35883     hide : function(){
35884         //if(!this.collapsed){
35885             this.el.dom.style.left = "-2000px";
35886             this.el.hide();
35887         //}else{
35888          //   this.collapsedEl.dom.style.left = "-2000px";
35889          //   this.collapsedEl.hide();
35890        // }
35891         this.visible = false;
35892         this.fireEvent("visibilitychange", this, false);
35893     },
35894
35895     /**
35896      * Shows this region if it was previously hidden.
35897      */
35898     show : function(){
35899         //if(!this.collapsed){
35900             this.el.show();
35901         //}else{
35902         //    this.collapsedEl.show();
35903        // }
35904         this.visible = true;
35905         this.fireEvent("visibilitychange", this, true);
35906     },
35907 /*
35908     closeClicked : function(){
35909         if(this.activePanel){
35910             this.remove(this.activePanel);
35911         }
35912     },
35913
35914     collapseClick : function(e){
35915         if(this.isSlid){
35916            e.stopPropagation();
35917            this.slideIn();
35918         }else{
35919            e.stopPropagation();
35920            this.slideOut();
35921         }
35922     },
35923 */
35924     /**
35925      * Collapses this region.
35926      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35927      */
35928     /*
35929     collapse : function(skipAnim, skipCheck = false){
35930         if(this.collapsed) {
35931             return;
35932         }
35933         
35934         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35935             
35936             this.collapsed = true;
35937             if(this.split){
35938                 this.split.el.hide();
35939             }
35940             if(this.config.animate && skipAnim !== true){
35941                 this.fireEvent("invalidated", this);
35942                 this.animateCollapse();
35943             }else{
35944                 this.el.setLocation(-20000,-20000);
35945                 this.el.hide();
35946                 this.collapsedEl.show();
35947                 this.fireEvent("collapsed", this);
35948                 this.fireEvent("invalidated", this);
35949             }
35950         }
35951         
35952     },
35953 */
35954     animateCollapse : function(){
35955         // overridden
35956     },
35957
35958     /**
35959      * Expands this region if it was previously collapsed.
35960      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35961      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35962      */
35963     /*
35964     expand : function(e, skipAnim){
35965         if(e) {
35966             e.stopPropagation();
35967         }
35968         if(!this.collapsed || this.el.hasActiveFx()) {
35969             return;
35970         }
35971         if(this.isSlid){
35972             this.afterSlideIn();
35973             skipAnim = true;
35974         }
35975         this.collapsed = false;
35976         if(this.config.animate && skipAnim !== true){
35977             this.animateExpand();
35978         }else{
35979             this.el.show();
35980             if(this.split){
35981                 this.split.el.show();
35982             }
35983             this.collapsedEl.setLocation(-2000,-2000);
35984             this.collapsedEl.hide();
35985             this.fireEvent("invalidated", this);
35986             this.fireEvent("expanded", this);
35987         }
35988     },
35989 */
35990     animateExpand : function(){
35991         // overridden
35992     },
35993
35994     initTabs : function()
35995     {
35996         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35997         
35998         var ts = new Roo.bootstrap.panel.Tabs({
35999                 el: this.bodyEl.dom,
36000                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36001                 disableTooltips: this.config.disableTabTips,
36002                 toolbar : this.config.toolbar
36003             });
36004         
36005         if(this.config.hideTabs){
36006             ts.stripWrap.setDisplayed(false);
36007         }
36008         this.tabs = ts;
36009         ts.resizeTabs = this.config.resizeTabs === true;
36010         ts.minTabWidth = this.config.minTabWidth || 40;
36011         ts.maxTabWidth = this.config.maxTabWidth || 250;
36012         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36013         ts.monitorResize = false;
36014         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36015         ts.bodyEl.addClass('roo-layout-tabs-body');
36016         this.panels.each(this.initPanelAsTab, this);
36017     },
36018
36019     initPanelAsTab : function(panel){
36020         var ti = this.tabs.addTab(
36021             panel.getEl().id,
36022             panel.getTitle(),
36023             null,
36024             this.config.closeOnTab && panel.isClosable(),
36025             panel.tpl
36026         );
36027         if(panel.tabTip !== undefined){
36028             ti.setTooltip(panel.tabTip);
36029         }
36030         ti.on("activate", function(){
36031               this.setActivePanel(panel);
36032         }, this);
36033         
36034         if(this.config.closeOnTab){
36035             ti.on("beforeclose", function(t, e){
36036                 e.cancel = true;
36037                 this.remove(panel);
36038             }, this);
36039         }
36040         
36041         panel.tabItem = ti;
36042         
36043         return ti;
36044     },
36045
36046     updatePanelTitle : function(panel, title)
36047     {
36048         if(this.activePanel == panel){
36049             this.updateTitle(title);
36050         }
36051         if(this.tabs){
36052             var ti = this.tabs.getTab(panel.getEl().id);
36053             ti.setText(title);
36054             if(panel.tabTip !== undefined){
36055                 ti.setTooltip(panel.tabTip);
36056             }
36057         }
36058     },
36059
36060     updateTitle : function(title){
36061         if(this.titleTextEl && !this.config.title){
36062             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36063         }
36064     },
36065
36066     setActivePanel : function(panel)
36067     {
36068         panel = this.getPanel(panel);
36069         if(this.activePanel && this.activePanel != panel){
36070             if(this.activePanel.setActiveState(false) === false){
36071                 return;
36072             }
36073         }
36074         this.activePanel = panel;
36075         panel.setActiveState(true);
36076         if(this.panelSize){
36077             panel.setSize(this.panelSize.width, this.panelSize.height);
36078         }
36079         if(this.closeBtn){
36080             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36081         }
36082         this.updateTitle(panel.getTitle());
36083         if(this.tabs){
36084             this.fireEvent("invalidated", this);
36085         }
36086         this.fireEvent("panelactivated", this, panel);
36087     },
36088
36089     /**
36090      * Shows the specified panel.
36091      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36092      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36093      */
36094     showPanel : function(panel)
36095     {
36096         panel = this.getPanel(panel);
36097         if(panel){
36098             if(this.tabs){
36099                 var tab = this.tabs.getTab(panel.getEl().id);
36100                 if(tab.isHidden()){
36101                     this.tabs.unhideTab(tab.id);
36102                 }
36103                 tab.activate();
36104             }else{
36105                 this.setActivePanel(panel);
36106             }
36107         }
36108         return panel;
36109     },
36110
36111     /**
36112      * Get the active panel for this region.
36113      * @return {Roo.ContentPanel} The active panel or null
36114      */
36115     getActivePanel : function(){
36116         return this.activePanel;
36117     },
36118
36119     validateVisibility : function(){
36120         if(this.panels.getCount() < 1){
36121             this.updateTitle("&#160;");
36122             this.closeBtn.hide();
36123             this.hide();
36124         }else{
36125             if(!this.isVisible()){
36126                 this.show();
36127             }
36128         }
36129     },
36130
36131     /**
36132      * Adds the passed ContentPanel(s) to this region.
36133      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36134      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36135      */
36136     add : function(panel)
36137     {
36138         if(arguments.length > 1){
36139             for(var i = 0, len = arguments.length; i < len; i++) {
36140                 this.add(arguments[i]);
36141             }
36142             return null;
36143         }
36144         
36145         // if we have not been rendered yet, then we can not really do much of this..
36146         if (!this.bodyEl) {
36147             this.unrendered_panels.push(panel);
36148             return panel;
36149         }
36150         
36151         
36152         
36153         
36154         if(this.hasPanel(panel)){
36155             this.showPanel(panel);
36156             return panel;
36157         }
36158         panel.setRegion(this);
36159         this.panels.add(panel);
36160        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36161             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36162             // and hide them... ???
36163             this.bodyEl.dom.appendChild(panel.getEl().dom);
36164             if(panel.background !== true){
36165                 this.setActivePanel(panel);
36166             }
36167             this.fireEvent("paneladded", this, panel);
36168             return panel;
36169         }
36170         */
36171         if(!this.tabs){
36172             this.initTabs();
36173         }else{
36174             this.initPanelAsTab(panel);
36175         }
36176         
36177         
36178         if(panel.background !== true){
36179             this.tabs.activate(panel.getEl().id);
36180         }
36181         this.fireEvent("paneladded", this, panel);
36182         return panel;
36183     },
36184
36185     /**
36186      * Hides the tab for the specified panel.
36187      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36188      */
36189     hidePanel : function(panel){
36190         if(this.tabs && (panel = this.getPanel(panel))){
36191             this.tabs.hideTab(panel.getEl().id);
36192         }
36193     },
36194
36195     /**
36196      * Unhides the tab for a previously hidden panel.
36197      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36198      */
36199     unhidePanel : function(panel){
36200         if(this.tabs && (panel = this.getPanel(panel))){
36201             this.tabs.unhideTab(panel.getEl().id);
36202         }
36203     },
36204
36205     clearPanels : function(){
36206         while(this.panels.getCount() > 0){
36207              this.remove(this.panels.first());
36208         }
36209     },
36210
36211     /**
36212      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36213      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36214      * @param {Boolean} preservePanel Overrides the config preservePanel option
36215      * @return {Roo.ContentPanel} The panel that was removed
36216      */
36217     remove : function(panel, preservePanel)
36218     {
36219         panel = this.getPanel(panel);
36220         if(!panel){
36221             return null;
36222         }
36223         var e = {};
36224         this.fireEvent("beforeremove", this, panel, e);
36225         if(e.cancel === true){
36226             return null;
36227         }
36228         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36229         var panelId = panel.getId();
36230         this.panels.removeKey(panelId);
36231         if(preservePanel){
36232             document.body.appendChild(panel.getEl().dom);
36233         }
36234         if(this.tabs){
36235             this.tabs.removeTab(panel.getEl().id);
36236         }else if (!preservePanel){
36237             this.bodyEl.dom.removeChild(panel.getEl().dom);
36238         }
36239         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36240             var p = this.panels.first();
36241             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36242             tempEl.appendChild(p.getEl().dom);
36243             this.bodyEl.update("");
36244             this.bodyEl.dom.appendChild(p.getEl().dom);
36245             tempEl = null;
36246             this.updateTitle(p.getTitle());
36247             this.tabs = null;
36248             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36249             this.setActivePanel(p);
36250         }
36251         panel.setRegion(null);
36252         if(this.activePanel == panel){
36253             this.activePanel = null;
36254         }
36255         if(this.config.autoDestroy !== false && preservePanel !== true){
36256             try{panel.destroy();}catch(e){}
36257         }
36258         this.fireEvent("panelremoved", this, panel);
36259         return panel;
36260     },
36261
36262     /**
36263      * Returns the TabPanel component used by this region
36264      * @return {Roo.TabPanel}
36265      */
36266     getTabs : function(){
36267         return this.tabs;
36268     },
36269
36270     createTool : function(parentEl, className){
36271         var btn = Roo.DomHelper.append(parentEl, {
36272             tag: "div",
36273             cls: "x-layout-tools-button",
36274             children: [ {
36275                 tag: "div",
36276                 cls: "roo-layout-tools-button-inner " + className,
36277                 html: "&#160;"
36278             }]
36279         }, true);
36280         btn.addClassOnOver("roo-layout-tools-button-over");
36281         return btn;
36282     }
36283 });/*
36284  * Based on:
36285  * Ext JS Library 1.1.1
36286  * Copyright(c) 2006-2007, Ext JS, LLC.
36287  *
36288  * Originally Released Under LGPL - original licence link has changed is not relivant.
36289  *
36290  * Fork - LGPL
36291  * <script type="text/javascript">
36292  */
36293  
36294
36295
36296 /**
36297  * @class Roo.SplitLayoutRegion
36298  * @extends Roo.LayoutRegion
36299  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36300  */
36301 Roo.bootstrap.layout.Split = function(config){
36302     this.cursor = config.cursor;
36303     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36304 };
36305
36306 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36307 {
36308     splitTip : "Drag to resize.",
36309     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36310     useSplitTips : false,
36311
36312     applyConfig : function(config){
36313         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36314     },
36315     
36316     onRender : function(ctr,pos) {
36317         
36318         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36319         if(!this.config.split){
36320             return;
36321         }
36322         if(!this.split){
36323             
36324             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36325                             tag: "div",
36326                             id: this.el.id + "-split",
36327                             cls: "roo-layout-split roo-layout-split-"+this.position,
36328                             html: "&#160;"
36329             });
36330             /** The SplitBar for this region 
36331             * @type Roo.SplitBar */
36332             // does not exist yet...
36333             Roo.log([this.position, this.orientation]);
36334             
36335             this.split = new Roo.bootstrap.SplitBar({
36336                 dragElement : splitEl,
36337                 resizingElement: this.el,
36338                 orientation : this.orientation
36339             });
36340             
36341             this.split.on("moved", this.onSplitMove, this);
36342             this.split.useShim = this.config.useShim === true;
36343             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36344             if(this.useSplitTips){
36345                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36346             }
36347             //if(config.collapsible){
36348             //    this.split.el.on("dblclick", this.collapse,  this);
36349             //}
36350         }
36351         if(typeof this.config.minSize != "undefined"){
36352             this.split.minSize = this.config.minSize;
36353         }
36354         if(typeof this.config.maxSize != "undefined"){
36355             this.split.maxSize = this.config.maxSize;
36356         }
36357         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36358             this.hideSplitter();
36359         }
36360         
36361     },
36362
36363     getHMaxSize : function(){
36364          var cmax = this.config.maxSize || 10000;
36365          var center = this.mgr.getRegion("center");
36366          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36367     },
36368
36369     getVMaxSize : function(){
36370          var cmax = this.config.maxSize || 10000;
36371          var center = this.mgr.getRegion("center");
36372          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36373     },
36374
36375     onSplitMove : function(split, newSize){
36376         this.fireEvent("resized", this, newSize);
36377     },
36378     
36379     /** 
36380      * Returns the {@link Roo.SplitBar} for this region.
36381      * @return {Roo.SplitBar}
36382      */
36383     getSplitBar : function(){
36384         return this.split;
36385     },
36386     
36387     hide : function(){
36388         this.hideSplitter();
36389         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36390     },
36391
36392     hideSplitter : function(){
36393         if(this.split){
36394             this.split.el.setLocation(-2000,-2000);
36395             this.split.el.hide();
36396         }
36397     },
36398
36399     show : function(){
36400         if(this.split){
36401             this.split.el.show();
36402         }
36403         Roo.bootstrap.layout.Split.superclass.show.call(this);
36404     },
36405     
36406     beforeSlide: function(){
36407         if(Roo.isGecko){// firefox overflow auto bug workaround
36408             this.bodyEl.clip();
36409             if(this.tabs) {
36410                 this.tabs.bodyEl.clip();
36411             }
36412             if(this.activePanel){
36413                 this.activePanel.getEl().clip();
36414                 
36415                 if(this.activePanel.beforeSlide){
36416                     this.activePanel.beforeSlide();
36417                 }
36418             }
36419         }
36420     },
36421     
36422     afterSlide : function(){
36423         if(Roo.isGecko){// firefox overflow auto bug workaround
36424             this.bodyEl.unclip();
36425             if(this.tabs) {
36426                 this.tabs.bodyEl.unclip();
36427             }
36428             if(this.activePanel){
36429                 this.activePanel.getEl().unclip();
36430                 if(this.activePanel.afterSlide){
36431                     this.activePanel.afterSlide();
36432                 }
36433             }
36434         }
36435     },
36436
36437     initAutoHide : function(){
36438         if(this.autoHide !== false){
36439             if(!this.autoHideHd){
36440                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36441                 this.autoHideHd = {
36442                     "mouseout": function(e){
36443                         if(!e.within(this.el, true)){
36444                             st.delay(500);
36445                         }
36446                     },
36447                     "mouseover" : function(e){
36448                         st.cancel();
36449                     },
36450                     scope : this
36451                 };
36452             }
36453             this.el.on(this.autoHideHd);
36454         }
36455     },
36456
36457     clearAutoHide : function(){
36458         if(this.autoHide !== false){
36459             this.el.un("mouseout", this.autoHideHd.mouseout);
36460             this.el.un("mouseover", this.autoHideHd.mouseover);
36461         }
36462     },
36463
36464     clearMonitor : function(){
36465         Roo.get(document).un("click", this.slideInIf, this);
36466     },
36467
36468     // these names are backwards but not changed for compat
36469     slideOut : function(){
36470         if(this.isSlid || this.el.hasActiveFx()){
36471             return;
36472         }
36473         this.isSlid = true;
36474         if(this.collapseBtn){
36475             this.collapseBtn.hide();
36476         }
36477         this.closeBtnState = this.closeBtn.getStyle('display');
36478         this.closeBtn.hide();
36479         if(this.stickBtn){
36480             this.stickBtn.show();
36481         }
36482         this.el.show();
36483         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36484         this.beforeSlide();
36485         this.el.setStyle("z-index", 10001);
36486         this.el.slideIn(this.getSlideAnchor(), {
36487             callback: function(){
36488                 this.afterSlide();
36489                 this.initAutoHide();
36490                 Roo.get(document).on("click", this.slideInIf, this);
36491                 this.fireEvent("slideshow", this);
36492             },
36493             scope: this,
36494             block: true
36495         });
36496     },
36497
36498     afterSlideIn : function(){
36499         this.clearAutoHide();
36500         this.isSlid = false;
36501         this.clearMonitor();
36502         this.el.setStyle("z-index", "");
36503         if(this.collapseBtn){
36504             this.collapseBtn.show();
36505         }
36506         this.closeBtn.setStyle('display', this.closeBtnState);
36507         if(this.stickBtn){
36508             this.stickBtn.hide();
36509         }
36510         this.fireEvent("slidehide", this);
36511     },
36512
36513     slideIn : function(cb){
36514         if(!this.isSlid || this.el.hasActiveFx()){
36515             Roo.callback(cb);
36516             return;
36517         }
36518         this.isSlid = false;
36519         this.beforeSlide();
36520         this.el.slideOut(this.getSlideAnchor(), {
36521             callback: function(){
36522                 this.el.setLeftTop(-10000, -10000);
36523                 this.afterSlide();
36524                 this.afterSlideIn();
36525                 Roo.callback(cb);
36526             },
36527             scope: this,
36528             block: true
36529         });
36530     },
36531     
36532     slideInIf : function(e){
36533         if(!e.within(this.el)){
36534             this.slideIn();
36535         }
36536     },
36537
36538     animateCollapse : function(){
36539         this.beforeSlide();
36540         this.el.setStyle("z-index", 20000);
36541         var anchor = this.getSlideAnchor();
36542         this.el.slideOut(anchor, {
36543             callback : function(){
36544                 this.el.setStyle("z-index", "");
36545                 this.collapsedEl.slideIn(anchor, {duration:.3});
36546                 this.afterSlide();
36547                 this.el.setLocation(-10000,-10000);
36548                 this.el.hide();
36549                 this.fireEvent("collapsed", this);
36550             },
36551             scope: this,
36552             block: true
36553         });
36554     },
36555
36556     animateExpand : function(){
36557         this.beforeSlide();
36558         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36559         this.el.setStyle("z-index", 20000);
36560         this.collapsedEl.hide({
36561             duration:.1
36562         });
36563         this.el.slideIn(this.getSlideAnchor(), {
36564             callback : function(){
36565                 this.el.setStyle("z-index", "");
36566                 this.afterSlide();
36567                 if(this.split){
36568                     this.split.el.show();
36569                 }
36570                 this.fireEvent("invalidated", this);
36571                 this.fireEvent("expanded", this);
36572             },
36573             scope: this,
36574             block: true
36575         });
36576     },
36577
36578     anchors : {
36579         "west" : "left",
36580         "east" : "right",
36581         "north" : "top",
36582         "south" : "bottom"
36583     },
36584
36585     sanchors : {
36586         "west" : "l",
36587         "east" : "r",
36588         "north" : "t",
36589         "south" : "b"
36590     },
36591
36592     canchors : {
36593         "west" : "tl-tr",
36594         "east" : "tr-tl",
36595         "north" : "tl-bl",
36596         "south" : "bl-tl"
36597     },
36598
36599     getAnchor : function(){
36600         return this.anchors[this.position];
36601     },
36602
36603     getCollapseAnchor : function(){
36604         return this.canchors[this.position];
36605     },
36606
36607     getSlideAnchor : function(){
36608         return this.sanchors[this.position];
36609     },
36610
36611     getAlignAdj : function(){
36612         var cm = this.cmargins;
36613         switch(this.position){
36614             case "west":
36615                 return [0, 0];
36616             break;
36617             case "east":
36618                 return [0, 0];
36619             break;
36620             case "north":
36621                 return [0, 0];
36622             break;
36623             case "south":
36624                 return [0, 0];
36625             break;
36626         }
36627     },
36628
36629     getExpandAdj : function(){
36630         var c = this.collapsedEl, cm = this.cmargins;
36631         switch(this.position){
36632             case "west":
36633                 return [-(cm.right+c.getWidth()+cm.left), 0];
36634             break;
36635             case "east":
36636                 return [cm.right+c.getWidth()+cm.left, 0];
36637             break;
36638             case "north":
36639                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36640             break;
36641             case "south":
36642                 return [0, cm.top+cm.bottom+c.getHeight()];
36643             break;
36644         }
36645     }
36646 });/*
36647  * Based on:
36648  * Ext JS Library 1.1.1
36649  * Copyright(c) 2006-2007, Ext JS, LLC.
36650  *
36651  * Originally Released Under LGPL - original licence link has changed is not relivant.
36652  *
36653  * Fork - LGPL
36654  * <script type="text/javascript">
36655  */
36656 /*
36657  * These classes are private internal classes
36658  */
36659 Roo.bootstrap.layout.Center = function(config){
36660     config.region = "center";
36661     Roo.bootstrap.layout.Region.call(this, config);
36662     this.visible = true;
36663     this.minWidth = config.minWidth || 20;
36664     this.minHeight = config.minHeight || 20;
36665 };
36666
36667 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36668     hide : function(){
36669         // center panel can't be hidden
36670     },
36671     
36672     show : function(){
36673         // center panel can't be hidden
36674     },
36675     
36676     getMinWidth: function(){
36677         return this.minWidth;
36678     },
36679     
36680     getMinHeight: function(){
36681         return this.minHeight;
36682     }
36683 });
36684
36685
36686
36687
36688  
36689
36690
36691
36692
36693
36694 Roo.bootstrap.layout.North = function(config)
36695 {
36696     config.region = 'north';
36697     config.cursor = 'n-resize';
36698     
36699     Roo.bootstrap.layout.Split.call(this, config);
36700     
36701     
36702     if(this.split){
36703         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36704         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36705         this.split.el.addClass("roo-layout-split-v");
36706     }
36707     var size = config.initialSize || config.height;
36708     if(typeof size != "undefined"){
36709         this.el.setHeight(size);
36710     }
36711 };
36712 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36713 {
36714     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36715     
36716     
36717     
36718     getBox : function(){
36719         if(this.collapsed){
36720             return this.collapsedEl.getBox();
36721         }
36722         var box = this.el.getBox();
36723         if(this.split){
36724             box.height += this.split.el.getHeight();
36725         }
36726         return box;
36727     },
36728     
36729     updateBox : function(box){
36730         if(this.split && !this.collapsed){
36731             box.height -= this.split.el.getHeight();
36732             this.split.el.setLeft(box.x);
36733             this.split.el.setTop(box.y+box.height);
36734             this.split.el.setWidth(box.width);
36735         }
36736         if(this.collapsed){
36737             this.updateBody(box.width, null);
36738         }
36739         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36740     }
36741 });
36742
36743
36744
36745
36746
36747 Roo.bootstrap.layout.South = function(config){
36748     config.region = 'south';
36749     config.cursor = 's-resize';
36750     Roo.bootstrap.layout.Split.call(this, config);
36751     if(this.split){
36752         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36753         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36754         this.split.el.addClass("roo-layout-split-v");
36755     }
36756     var size = config.initialSize || config.height;
36757     if(typeof size != "undefined"){
36758         this.el.setHeight(size);
36759     }
36760 };
36761
36762 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36763     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36764     getBox : function(){
36765         if(this.collapsed){
36766             return this.collapsedEl.getBox();
36767         }
36768         var box = this.el.getBox();
36769         if(this.split){
36770             var sh = this.split.el.getHeight();
36771             box.height += sh;
36772             box.y -= sh;
36773         }
36774         return box;
36775     },
36776     
36777     updateBox : function(box){
36778         if(this.split && !this.collapsed){
36779             var sh = this.split.el.getHeight();
36780             box.height -= sh;
36781             box.y += sh;
36782             this.split.el.setLeft(box.x);
36783             this.split.el.setTop(box.y-sh);
36784             this.split.el.setWidth(box.width);
36785         }
36786         if(this.collapsed){
36787             this.updateBody(box.width, null);
36788         }
36789         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36790     }
36791 });
36792
36793 Roo.bootstrap.layout.East = function(config){
36794     config.region = "east";
36795     config.cursor = "e-resize";
36796     Roo.bootstrap.layout.Split.call(this, config);
36797     if(this.split){
36798         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36799         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36800         this.split.el.addClass("roo-layout-split-h");
36801     }
36802     var size = config.initialSize || config.width;
36803     if(typeof size != "undefined"){
36804         this.el.setWidth(size);
36805     }
36806 };
36807 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36808     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36809     getBox : function(){
36810         if(this.collapsed){
36811             return this.collapsedEl.getBox();
36812         }
36813         var box = this.el.getBox();
36814         if(this.split){
36815             var sw = this.split.el.getWidth();
36816             box.width += sw;
36817             box.x -= sw;
36818         }
36819         return box;
36820     },
36821
36822     updateBox : function(box){
36823         if(this.split && !this.collapsed){
36824             var sw = this.split.el.getWidth();
36825             box.width -= sw;
36826             this.split.el.setLeft(box.x);
36827             this.split.el.setTop(box.y);
36828             this.split.el.setHeight(box.height);
36829             box.x += sw;
36830         }
36831         if(this.collapsed){
36832             this.updateBody(null, box.height);
36833         }
36834         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36835     }
36836 });
36837
36838 Roo.bootstrap.layout.West = function(config){
36839     config.region = "west";
36840     config.cursor = "w-resize";
36841     
36842     Roo.bootstrap.layout.Split.call(this, config);
36843     if(this.split){
36844         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36845         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36846         this.split.el.addClass("roo-layout-split-h");
36847     }
36848     
36849 };
36850 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36851     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36852     
36853     onRender: function(ctr, pos)
36854     {
36855         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36856         var size = this.config.initialSize || this.config.width;
36857         if(typeof size != "undefined"){
36858             this.el.setWidth(size);
36859         }
36860     },
36861     
36862     getBox : function(){
36863         if(this.collapsed){
36864             return this.collapsedEl.getBox();
36865         }
36866         var box = this.el.getBox();
36867         if(this.split){
36868             box.width += this.split.el.getWidth();
36869         }
36870         return box;
36871     },
36872     
36873     updateBox : function(box){
36874         if(this.split && !this.collapsed){
36875             var sw = this.split.el.getWidth();
36876             box.width -= sw;
36877             this.split.el.setLeft(box.x+box.width);
36878             this.split.el.setTop(box.y);
36879             this.split.el.setHeight(box.height);
36880         }
36881         if(this.collapsed){
36882             this.updateBody(null, box.height);
36883         }
36884         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36885     }
36886 });
36887 Roo.namespace("Roo.bootstrap.panel");/*
36888  * Based on:
36889  * Ext JS Library 1.1.1
36890  * Copyright(c) 2006-2007, Ext JS, LLC.
36891  *
36892  * Originally Released Under LGPL - original licence link has changed is not relivant.
36893  *
36894  * Fork - LGPL
36895  * <script type="text/javascript">
36896  */
36897 /**
36898  * @class Roo.ContentPanel
36899  * @extends Roo.util.Observable
36900  * A basic ContentPanel element.
36901  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36902  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36903  * @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
36904  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36905  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36906  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36907  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36908  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36909  * @cfg {String} title          The title for this panel
36910  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36911  * @cfg {String} url            Calls {@link #setUrl} with this value
36912  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36913  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36914  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36915  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36916  * @cfg {Boolean} badges render the badges
36917
36918  * @constructor
36919  * Create a new ContentPanel.
36920  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36921  * @param {String/Object} config A string to set only the title or a config object
36922  * @param {String} content (optional) Set the HTML content for this panel
36923  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36924  */
36925 Roo.bootstrap.panel.Content = function( config){
36926     
36927     this.tpl = config.tpl || false;
36928     
36929     var el = config.el;
36930     var content = config.content;
36931
36932     if(config.autoCreate){ // xtype is available if this is called from factory
36933         el = Roo.id();
36934     }
36935     this.el = Roo.get(el);
36936     if(!this.el && config && config.autoCreate){
36937         if(typeof config.autoCreate == "object"){
36938             if(!config.autoCreate.id){
36939                 config.autoCreate.id = config.id||el;
36940             }
36941             this.el = Roo.DomHelper.append(document.body,
36942                         config.autoCreate, true);
36943         }else{
36944             var elcfg =  {   tag: "div",
36945                             cls: "roo-layout-inactive-content",
36946                             id: config.id||el
36947                             };
36948             if (config.html) {
36949                 elcfg.html = config.html;
36950                 
36951             }
36952                         
36953             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36954         }
36955     } 
36956     this.closable = false;
36957     this.loaded = false;
36958     this.active = false;
36959    
36960       
36961     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36962         
36963         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36964         
36965         this.wrapEl = this.el; //this.el.wrap();
36966         var ti = [];
36967         if (config.toolbar.items) {
36968             ti = config.toolbar.items ;
36969             delete config.toolbar.items ;
36970         }
36971         
36972         var nitems = [];
36973         this.toolbar.render(this.wrapEl, 'before');
36974         for(var i =0;i < ti.length;i++) {
36975           //  Roo.log(['add child', items[i]]);
36976             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36977         }
36978         this.toolbar.items = nitems;
36979         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36980         delete config.toolbar;
36981         
36982     }
36983     /*
36984     // xtype created footer. - not sure if will work as we normally have to render first..
36985     if (this.footer && !this.footer.el && this.footer.xtype) {
36986         if (!this.wrapEl) {
36987             this.wrapEl = this.el.wrap();
36988         }
36989     
36990         this.footer.container = this.wrapEl.createChild();
36991          
36992         this.footer = Roo.factory(this.footer, Roo);
36993         
36994     }
36995     */
36996     
36997      if(typeof config == "string"){
36998         this.title = config;
36999     }else{
37000         Roo.apply(this, config);
37001     }
37002     
37003     if(this.resizeEl){
37004         this.resizeEl = Roo.get(this.resizeEl, true);
37005     }else{
37006         this.resizeEl = this.el;
37007     }
37008     // handle view.xtype
37009     
37010  
37011     
37012     
37013     this.addEvents({
37014         /**
37015          * @event activate
37016          * Fires when this panel is activated. 
37017          * @param {Roo.ContentPanel} this
37018          */
37019         "activate" : true,
37020         /**
37021          * @event deactivate
37022          * Fires when this panel is activated. 
37023          * @param {Roo.ContentPanel} this
37024          */
37025         "deactivate" : true,
37026
37027         /**
37028          * @event resize
37029          * Fires when this panel is resized if fitToFrame is true.
37030          * @param {Roo.ContentPanel} this
37031          * @param {Number} width The width after any component adjustments
37032          * @param {Number} height The height after any component adjustments
37033          */
37034         "resize" : true,
37035         
37036          /**
37037          * @event render
37038          * Fires when this tab is created
37039          * @param {Roo.ContentPanel} this
37040          */
37041         "render" : true
37042         
37043         
37044         
37045     });
37046     
37047
37048     
37049     
37050     if(this.autoScroll){
37051         this.resizeEl.setStyle("overflow", "auto");
37052     } else {
37053         // fix randome scrolling
37054         //this.el.on('scroll', function() {
37055         //    Roo.log('fix random scolling');
37056         //    this.scrollTo('top',0); 
37057         //});
37058     }
37059     content = content || this.content;
37060     if(content){
37061         this.setContent(content);
37062     }
37063     if(config && config.url){
37064         this.setUrl(this.url, this.params, this.loadOnce);
37065     }
37066     
37067     
37068     
37069     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37070     
37071     if (this.view && typeof(this.view.xtype) != 'undefined') {
37072         this.view.el = this.el.appendChild(document.createElement("div"));
37073         this.view = Roo.factory(this.view); 
37074         this.view.render  &&  this.view.render(false, '');  
37075     }
37076     
37077     
37078     this.fireEvent('render', this);
37079 };
37080
37081 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37082     
37083     tabTip : '',
37084     
37085     setRegion : function(region){
37086         this.region = region;
37087         this.setActiveClass(region && !this.background);
37088     },
37089     
37090     
37091     setActiveClass: function(state)
37092     {
37093         if(state){
37094            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37095            this.el.setStyle('position','relative');
37096         }else{
37097            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37098            this.el.setStyle('position', 'absolute');
37099         } 
37100     },
37101     
37102     /**
37103      * Returns the toolbar for this Panel if one was configured. 
37104      * @return {Roo.Toolbar} 
37105      */
37106     getToolbar : function(){
37107         return this.toolbar;
37108     },
37109     
37110     setActiveState : function(active)
37111     {
37112         this.active = active;
37113         this.setActiveClass(active);
37114         if(!active){
37115             if(this.fireEvent("deactivate", this) === false){
37116                 return false;
37117             }
37118             return true;
37119         }
37120         this.fireEvent("activate", this);
37121         return true;
37122     },
37123     /**
37124      * Updates this panel's element
37125      * @param {String} content The new content
37126      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37127     */
37128     setContent : function(content, loadScripts){
37129         this.el.update(content, loadScripts);
37130     },
37131
37132     ignoreResize : function(w, h){
37133         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37134             return true;
37135         }else{
37136             this.lastSize = {width: w, height: h};
37137             return false;
37138         }
37139     },
37140     /**
37141      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37142      * @return {Roo.UpdateManager} The UpdateManager
37143      */
37144     getUpdateManager : function(){
37145         return this.el.getUpdateManager();
37146     },
37147      /**
37148      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37149      * @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:
37150 <pre><code>
37151 panel.load({
37152     url: "your-url.php",
37153     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37154     callback: yourFunction,
37155     scope: yourObject, //(optional scope)
37156     discardUrl: false,
37157     nocache: false,
37158     text: "Loading...",
37159     timeout: 30,
37160     scripts: false
37161 });
37162 </code></pre>
37163      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37164      * 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.
37165      * @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}
37166      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37167      * @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.
37168      * @return {Roo.ContentPanel} this
37169      */
37170     load : function(){
37171         var um = this.el.getUpdateManager();
37172         um.update.apply(um, arguments);
37173         return this;
37174     },
37175
37176
37177     /**
37178      * 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.
37179      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37180      * @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)
37181      * @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)
37182      * @return {Roo.UpdateManager} The UpdateManager
37183      */
37184     setUrl : function(url, params, loadOnce){
37185         if(this.refreshDelegate){
37186             this.removeListener("activate", this.refreshDelegate);
37187         }
37188         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37189         this.on("activate", this.refreshDelegate);
37190         return this.el.getUpdateManager();
37191     },
37192     
37193     _handleRefresh : function(url, params, loadOnce){
37194         if(!loadOnce || !this.loaded){
37195             var updater = this.el.getUpdateManager();
37196             updater.update(url, params, this._setLoaded.createDelegate(this));
37197         }
37198     },
37199     
37200     _setLoaded : function(){
37201         this.loaded = true;
37202     }, 
37203     
37204     /**
37205      * Returns this panel's id
37206      * @return {String} 
37207      */
37208     getId : function(){
37209         return this.el.id;
37210     },
37211     
37212     /** 
37213      * Returns this panel's element - used by regiosn to add.
37214      * @return {Roo.Element} 
37215      */
37216     getEl : function(){
37217         return this.wrapEl || this.el;
37218     },
37219     
37220    
37221     
37222     adjustForComponents : function(width, height)
37223     {
37224         //Roo.log('adjustForComponents ');
37225         if(this.resizeEl != this.el){
37226             width -= this.el.getFrameWidth('lr');
37227             height -= this.el.getFrameWidth('tb');
37228         }
37229         if(this.toolbar){
37230             var te = this.toolbar.getEl();
37231             te.setWidth(width);
37232             height -= te.getHeight();
37233         }
37234         if(this.footer){
37235             var te = this.footer.getEl();
37236             te.setWidth(width);
37237             height -= te.getHeight();
37238         }
37239         
37240         
37241         if(this.adjustments){
37242             width += this.adjustments[0];
37243             height += this.adjustments[1];
37244         }
37245         return {"width": width, "height": height};
37246     },
37247     
37248     setSize : function(width, height){
37249         if(this.fitToFrame && !this.ignoreResize(width, height)){
37250             if(this.fitContainer && this.resizeEl != this.el){
37251                 this.el.setSize(width, height);
37252             }
37253             var size = this.adjustForComponents(width, height);
37254             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37255             this.fireEvent('resize', this, size.width, size.height);
37256         }
37257     },
37258     
37259     /**
37260      * Returns this panel's title
37261      * @return {String} 
37262      */
37263     getTitle : function(){
37264         
37265         if (typeof(this.title) != 'object') {
37266             return this.title;
37267         }
37268         
37269         var t = '';
37270         for (var k in this.title) {
37271             if (!this.title.hasOwnProperty(k)) {
37272                 continue;
37273             }
37274             
37275             if (k.indexOf('-') >= 0) {
37276                 var s = k.split('-');
37277                 for (var i = 0; i<s.length; i++) {
37278                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37279                 }
37280             } else {
37281                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37282             }
37283         }
37284         return t;
37285     },
37286     
37287     /**
37288      * Set this panel's title
37289      * @param {String} title
37290      */
37291     setTitle : function(title){
37292         this.title = title;
37293         if(this.region){
37294             this.region.updatePanelTitle(this, title);
37295         }
37296     },
37297     
37298     /**
37299      * Returns true is this panel was configured to be closable
37300      * @return {Boolean} 
37301      */
37302     isClosable : function(){
37303         return this.closable;
37304     },
37305     
37306     beforeSlide : function(){
37307         this.el.clip();
37308         this.resizeEl.clip();
37309     },
37310     
37311     afterSlide : function(){
37312         this.el.unclip();
37313         this.resizeEl.unclip();
37314     },
37315     
37316     /**
37317      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37318      *   Will fail silently if the {@link #setUrl} method has not been called.
37319      *   This does not activate the panel, just updates its content.
37320      */
37321     refresh : function(){
37322         if(this.refreshDelegate){
37323            this.loaded = false;
37324            this.refreshDelegate();
37325         }
37326     },
37327     
37328     /**
37329      * Destroys this panel
37330      */
37331     destroy : function(){
37332         this.el.removeAllListeners();
37333         var tempEl = document.createElement("span");
37334         tempEl.appendChild(this.el.dom);
37335         tempEl.innerHTML = "";
37336         this.el.remove();
37337         this.el = null;
37338     },
37339     
37340     /**
37341      * form - if the content panel contains a form - this is a reference to it.
37342      * @type {Roo.form.Form}
37343      */
37344     form : false,
37345     /**
37346      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37347      *    This contains a reference to it.
37348      * @type {Roo.View}
37349      */
37350     view : false,
37351     
37352       /**
37353      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37354      * <pre><code>
37355
37356 layout.addxtype({
37357        xtype : 'Form',
37358        items: [ .... ]
37359    }
37360 );
37361
37362 </code></pre>
37363      * @param {Object} cfg Xtype definition of item to add.
37364      */
37365     
37366     
37367     getChildContainer: function () {
37368         return this.getEl();
37369     }
37370     
37371     
37372     /*
37373         var  ret = new Roo.factory(cfg);
37374         return ret;
37375         
37376         
37377         // add form..
37378         if (cfg.xtype.match(/^Form$/)) {
37379             
37380             var el;
37381             //if (this.footer) {
37382             //    el = this.footer.container.insertSibling(false, 'before');
37383             //} else {
37384                 el = this.el.createChild();
37385             //}
37386
37387             this.form = new  Roo.form.Form(cfg);
37388             
37389             
37390             if ( this.form.allItems.length) {
37391                 this.form.render(el.dom);
37392             }
37393             return this.form;
37394         }
37395         // should only have one of theses..
37396         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37397             // views.. should not be just added - used named prop 'view''
37398             
37399             cfg.el = this.el.appendChild(document.createElement("div"));
37400             // factory?
37401             
37402             var ret = new Roo.factory(cfg);
37403              
37404              ret.render && ret.render(false, ''); // render blank..
37405             this.view = ret;
37406             return ret;
37407         }
37408         return false;
37409     }
37410     \*/
37411 });
37412  
37413 /**
37414  * @class Roo.bootstrap.panel.Grid
37415  * @extends Roo.bootstrap.panel.Content
37416  * @constructor
37417  * Create a new GridPanel.
37418  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37419  * @param {Object} config A the config object
37420   
37421  */
37422
37423
37424
37425 Roo.bootstrap.panel.Grid = function(config)
37426 {
37427     
37428       
37429     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37430         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37431
37432     config.el = this.wrapper;
37433     //this.el = this.wrapper;
37434     
37435       if (config.container) {
37436         // ctor'ed from a Border/panel.grid
37437         
37438         
37439         this.wrapper.setStyle("overflow", "hidden");
37440         this.wrapper.addClass('roo-grid-container');
37441
37442     }
37443     
37444     
37445     if(config.toolbar){
37446         var tool_el = this.wrapper.createChild();    
37447         this.toolbar = Roo.factory(config.toolbar);
37448         var ti = [];
37449         if (config.toolbar.items) {
37450             ti = config.toolbar.items ;
37451             delete config.toolbar.items ;
37452         }
37453         
37454         var nitems = [];
37455         this.toolbar.render(tool_el);
37456         for(var i =0;i < ti.length;i++) {
37457           //  Roo.log(['add child', items[i]]);
37458             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37459         }
37460         this.toolbar.items = nitems;
37461         
37462         delete config.toolbar;
37463     }
37464     
37465     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37466     config.grid.scrollBody = true;;
37467     config.grid.monitorWindowResize = false; // turn off autosizing
37468     config.grid.autoHeight = false;
37469     config.grid.autoWidth = false;
37470     
37471     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37472     
37473     if (config.background) {
37474         // render grid on panel activation (if panel background)
37475         this.on('activate', function(gp) {
37476             if (!gp.grid.rendered) {
37477                 gp.grid.render(this.wrapper);
37478                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37479             }
37480         });
37481             
37482     } else {
37483         this.grid.render(this.wrapper);
37484         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37485
37486     }
37487     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37488     // ??? needed ??? config.el = this.wrapper;
37489     
37490     
37491     
37492   
37493     // xtype created footer. - not sure if will work as we normally have to render first..
37494     if (this.footer && !this.footer.el && this.footer.xtype) {
37495         
37496         var ctr = this.grid.getView().getFooterPanel(true);
37497         this.footer.dataSource = this.grid.dataSource;
37498         this.footer = Roo.factory(this.footer, Roo);
37499         this.footer.render(ctr);
37500         
37501     }
37502     
37503     
37504     
37505     
37506      
37507 };
37508
37509 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37510     getId : function(){
37511         return this.grid.id;
37512     },
37513     
37514     /**
37515      * Returns the grid for this panel
37516      * @return {Roo.bootstrap.Table} 
37517      */
37518     getGrid : function(){
37519         return this.grid;    
37520     },
37521     
37522     setSize : function(width, height){
37523         if(!this.ignoreResize(width, height)){
37524             var grid = this.grid;
37525             var size = this.adjustForComponents(width, height);
37526             var gridel = grid.getGridEl();
37527             gridel.setSize(size.width, size.height);
37528             /*
37529             var thd = grid.getGridEl().select('thead',true).first();
37530             var tbd = grid.getGridEl().select('tbody', true).first();
37531             if (tbd) {
37532                 tbd.setSize(width, height - thd.getHeight());
37533             }
37534             */
37535             grid.autoSize();
37536         }
37537     },
37538      
37539     
37540     
37541     beforeSlide : function(){
37542         this.grid.getView().scroller.clip();
37543     },
37544     
37545     afterSlide : function(){
37546         this.grid.getView().scroller.unclip();
37547     },
37548     
37549     destroy : function(){
37550         this.grid.destroy();
37551         delete this.grid;
37552         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37553     }
37554 });
37555
37556 /**
37557  * @class Roo.bootstrap.panel.Nest
37558  * @extends Roo.bootstrap.panel.Content
37559  * @constructor
37560  * Create a new Panel, that can contain a layout.Border.
37561  * 
37562  * 
37563  * @param {Roo.BorderLayout} layout The layout for this panel
37564  * @param {String/Object} config A string to set only the title or a config object
37565  */
37566 Roo.bootstrap.panel.Nest = function(config)
37567 {
37568     // construct with only one argument..
37569     /* FIXME - implement nicer consturctors
37570     if (layout.layout) {
37571         config = layout;
37572         layout = config.layout;
37573         delete config.layout;
37574     }
37575     if (layout.xtype && !layout.getEl) {
37576         // then layout needs constructing..
37577         layout = Roo.factory(layout, Roo);
37578     }
37579     */
37580     
37581     config.el =  config.layout.getEl();
37582     
37583     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37584     
37585     config.layout.monitorWindowResize = false; // turn off autosizing
37586     this.layout = config.layout;
37587     this.layout.getEl().addClass("roo-layout-nested-layout");
37588     
37589     
37590     
37591     
37592 };
37593
37594 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37595
37596     setSize : function(width, height){
37597         if(!this.ignoreResize(width, height)){
37598             var size = this.adjustForComponents(width, height);
37599             var el = this.layout.getEl();
37600             if (size.height < 1) {
37601                 el.setWidth(size.width);   
37602             } else {
37603                 el.setSize(size.width, size.height);
37604             }
37605             var touch = el.dom.offsetWidth;
37606             this.layout.layout();
37607             // ie requires a double layout on the first pass
37608             if(Roo.isIE && !this.initialized){
37609                 this.initialized = true;
37610                 this.layout.layout();
37611             }
37612         }
37613     },
37614     
37615     // activate all subpanels if not currently active..
37616     
37617     setActiveState : function(active){
37618         this.active = active;
37619         this.setActiveClass(active);
37620         
37621         if(!active){
37622             this.fireEvent("deactivate", this);
37623             return;
37624         }
37625         
37626         this.fireEvent("activate", this);
37627         // not sure if this should happen before or after..
37628         if (!this.layout) {
37629             return; // should not happen..
37630         }
37631         var reg = false;
37632         for (var r in this.layout.regions) {
37633             reg = this.layout.getRegion(r);
37634             if (reg.getActivePanel()) {
37635                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37636                 reg.setActivePanel(reg.getActivePanel());
37637                 continue;
37638             }
37639             if (!reg.panels.length) {
37640                 continue;
37641             }
37642             reg.showPanel(reg.getPanel(0));
37643         }
37644         
37645         
37646         
37647         
37648     },
37649     
37650     /**
37651      * Returns the nested BorderLayout for this panel
37652      * @return {Roo.BorderLayout} 
37653      */
37654     getLayout : function(){
37655         return this.layout;
37656     },
37657     
37658      /**
37659      * Adds a xtype elements to the layout of the nested panel
37660      * <pre><code>
37661
37662 panel.addxtype({
37663        xtype : 'ContentPanel',
37664        region: 'west',
37665        items: [ .... ]
37666    }
37667 );
37668
37669 panel.addxtype({
37670         xtype : 'NestedLayoutPanel',
37671         region: 'west',
37672         layout: {
37673            center: { },
37674            west: { }   
37675         },
37676         items : [ ... list of content panels or nested layout panels.. ]
37677    }
37678 );
37679 </code></pre>
37680      * @param {Object} cfg Xtype definition of item to add.
37681      */
37682     addxtype : function(cfg) {
37683         return this.layout.addxtype(cfg);
37684     
37685     }
37686 });        /*
37687  * Based on:
37688  * Ext JS Library 1.1.1
37689  * Copyright(c) 2006-2007, Ext JS, LLC.
37690  *
37691  * Originally Released Under LGPL - original licence link has changed is not relivant.
37692  *
37693  * Fork - LGPL
37694  * <script type="text/javascript">
37695  */
37696 /**
37697  * @class Roo.TabPanel
37698  * @extends Roo.util.Observable
37699  * A lightweight tab container.
37700  * <br><br>
37701  * Usage:
37702  * <pre><code>
37703 // basic tabs 1, built from existing content
37704 var tabs = new Roo.TabPanel("tabs1");
37705 tabs.addTab("script", "View Script");
37706 tabs.addTab("markup", "View Markup");
37707 tabs.activate("script");
37708
37709 // more advanced tabs, built from javascript
37710 var jtabs = new Roo.TabPanel("jtabs");
37711 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37712
37713 // set up the UpdateManager
37714 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37715 var updater = tab2.getUpdateManager();
37716 updater.setDefaultUrl("ajax1.htm");
37717 tab2.on('activate', updater.refresh, updater, true);
37718
37719 // Use setUrl for Ajax loading
37720 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37721 tab3.setUrl("ajax2.htm", null, true);
37722
37723 // Disabled tab
37724 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37725 tab4.disable();
37726
37727 jtabs.activate("jtabs-1");
37728  * </code></pre>
37729  * @constructor
37730  * Create a new TabPanel.
37731  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37732  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37733  */
37734 Roo.bootstrap.panel.Tabs = function(config){
37735     /**
37736     * The container element for this TabPanel.
37737     * @type Roo.Element
37738     */
37739     this.el = Roo.get(config.el);
37740     delete config.el;
37741     if(config){
37742         if(typeof config == "boolean"){
37743             this.tabPosition = config ? "bottom" : "top";
37744         }else{
37745             Roo.apply(this, config);
37746         }
37747     }
37748     
37749     if(this.tabPosition == "bottom"){
37750         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37751         this.el.addClass("roo-tabs-bottom");
37752     }
37753     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37754     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37755     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37756     if(Roo.isIE){
37757         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37758     }
37759     if(this.tabPosition != "bottom"){
37760         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37761          * @type Roo.Element
37762          */
37763         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37764         this.el.addClass("roo-tabs-top");
37765     }
37766     this.items = [];
37767
37768     this.bodyEl.setStyle("position", "relative");
37769
37770     this.active = null;
37771     this.activateDelegate = this.activate.createDelegate(this);
37772
37773     this.addEvents({
37774         /**
37775          * @event tabchange
37776          * Fires when the active tab changes
37777          * @param {Roo.TabPanel} this
37778          * @param {Roo.TabPanelItem} activePanel The new active tab
37779          */
37780         "tabchange": true,
37781         /**
37782          * @event beforetabchange
37783          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37784          * @param {Roo.TabPanel} this
37785          * @param {Object} e Set cancel to true on this object to cancel the tab change
37786          * @param {Roo.TabPanelItem} tab The tab being changed to
37787          */
37788         "beforetabchange" : true
37789     });
37790
37791     Roo.EventManager.onWindowResize(this.onResize, this);
37792     this.cpad = this.el.getPadding("lr");
37793     this.hiddenCount = 0;
37794
37795
37796     // toolbar on the tabbar support...
37797     if (this.toolbar) {
37798         alert("no toolbar support yet");
37799         this.toolbar  = false;
37800         /*
37801         var tcfg = this.toolbar;
37802         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37803         this.toolbar = new Roo.Toolbar(tcfg);
37804         if (Roo.isSafari) {
37805             var tbl = tcfg.container.child('table', true);
37806             tbl.setAttribute('width', '100%');
37807         }
37808         */
37809         
37810     }
37811    
37812
37813
37814     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37815 };
37816
37817 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37818     /*
37819      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37820      */
37821     tabPosition : "top",
37822     /*
37823      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37824      */
37825     currentTabWidth : 0,
37826     /*
37827      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37828      */
37829     minTabWidth : 40,
37830     /*
37831      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37832      */
37833     maxTabWidth : 250,
37834     /*
37835      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37836      */
37837     preferredTabWidth : 175,
37838     /*
37839      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37840      */
37841     resizeTabs : false,
37842     /*
37843      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37844      */
37845     monitorResize : true,
37846     /*
37847      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37848      */
37849     toolbar : false,
37850
37851     /**
37852      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37853      * @param {String} id The id of the div to use <b>or create</b>
37854      * @param {String} text The text for the tab
37855      * @param {String} content (optional) Content to put in the TabPanelItem body
37856      * @param {Boolean} closable (optional) True to create a close icon on the tab
37857      * @return {Roo.TabPanelItem} The created TabPanelItem
37858      */
37859     addTab : function(id, text, content, closable, tpl)
37860     {
37861         var item = new Roo.bootstrap.panel.TabItem({
37862             panel: this,
37863             id : id,
37864             text : text,
37865             closable : closable,
37866             tpl : tpl
37867         });
37868         this.addTabItem(item);
37869         if(content){
37870             item.setContent(content);
37871         }
37872         return item;
37873     },
37874
37875     /**
37876      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37877      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37878      * @return {Roo.TabPanelItem}
37879      */
37880     getTab : function(id){
37881         return this.items[id];
37882     },
37883
37884     /**
37885      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37886      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37887      */
37888     hideTab : function(id){
37889         var t = this.items[id];
37890         if(!t.isHidden()){
37891            t.setHidden(true);
37892            this.hiddenCount++;
37893            this.autoSizeTabs();
37894         }
37895     },
37896
37897     /**
37898      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37899      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37900      */
37901     unhideTab : function(id){
37902         var t = this.items[id];
37903         if(t.isHidden()){
37904            t.setHidden(false);
37905            this.hiddenCount--;
37906            this.autoSizeTabs();
37907         }
37908     },
37909
37910     /**
37911      * Adds an existing {@link Roo.TabPanelItem}.
37912      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37913      */
37914     addTabItem : function(item){
37915         this.items[item.id] = item;
37916         this.items.push(item);
37917       //  if(this.resizeTabs){
37918     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37919   //         this.autoSizeTabs();
37920 //        }else{
37921 //            item.autoSize();
37922        // }
37923     },
37924
37925     /**
37926      * Removes a {@link Roo.TabPanelItem}.
37927      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37928      */
37929     removeTab : function(id){
37930         var items = this.items;
37931         var tab = items[id];
37932         if(!tab) { return; }
37933         var index = items.indexOf(tab);
37934         if(this.active == tab && items.length > 1){
37935             var newTab = this.getNextAvailable(index);
37936             if(newTab) {
37937                 newTab.activate();
37938             }
37939         }
37940         this.stripEl.dom.removeChild(tab.pnode.dom);
37941         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37942             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37943         }
37944         items.splice(index, 1);
37945         delete this.items[tab.id];
37946         tab.fireEvent("close", tab);
37947         tab.purgeListeners();
37948         this.autoSizeTabs();
37949     },
37950
37951     getNextAvailable : function(start){
37952         var items = this.items;
37953         var index = start;
37954         // look for a next tab that will slide over to
37955         // replace the one being removed
37956         while(index < items.length){
37957             var item = items[++index];
37958             if(item && !item.isHidden()){
37959                 return item;
37960             }
37961         }
37962         // if one isn't found select the previous tab (on the left)
37963         index = start;
37964         while(index >= 0){
37965             var item = items[--index];
37966             if(item && !item.isHidden()){
37967                 return item;
37968             }
37969         }
37970         return null;
37971     },
37972
37973     /**
37974      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37975      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37976      */
37977     disableTab : function(id){
37978         var tab = this.items[id];
37979         if(tab && this.active != tab){
37980             tab.disable();
37981         }
37982     },
37983
37984     /**
37985      * Enables a {@link Roo.TabPanelItem} that is disabled.
37986      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37987      */
37988     enableTab : function(id){
37989         var tab = this.items[id];
37990         tab.enable();
37991     },
37992
37993     /**
37994      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37995      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37996      * @return {Roo.TabPanelItem} The TabPanelItem.
37997      */
37998     activate : function(id){
37999         var tab = this.items[id];
38000         if(!tab){
38001             return null;
38002         }
38003         if(tab == this.active || tab.disabled){
38004             return tab;
38005         }
38006         var e = {};
38007         this.fireEvent("beforetabchange", this, e, tab);
38008         if(e.cancel !== true && !tab.disabled){
38009             if(this.active){
38010                 this.active.hide();
38011             }
38012             this.active = this.items[id];
38013             this.active.show();
38014             this.fireEvent("tabchange", this, this.active);
38015         }
38016         return tab;
38017     },
38018
38019     /**
38020      * Gets the active {@link Roo.TabPanelItem}.
38021      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38022      */
38023     getActiveTab : function(){
38024         return this.active;
38025     },
38026
38027     /**
38028      * Updates the tab body element to fit the height of the container element
38029      * for overflow scrolling
38030      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38031      */
38032     syncHeight : function(targetHeight){
38033         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38034         var bm = this.bodyEl.getMargins();
38035         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38036         this.bodyEl.setHeight(newHeight);
38037         return newHeight;
38038     },
38039
38040     onResize : function(){
38041         if(this.monitorResize){
38042             this.autoSizeTabs();
38043         }
38044     },
38045
38046     /**
38047      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38048      */
38049     beginUpdate : function(){
38050         this.updating = true;
38051     },
38052
38053     /**
38054      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38055      */
38056     endUpdate : function(){
38057         this.updating = false;
38058         this.autoSizeTabs();
38059     },
38060
38061     /**
38062      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38063      */
38064     autoSizeTabs : function(){
38065         var count = this.items.length;
38066         var vcount = count - this.hiddenCount;
38067         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38068             return;
38069         }
38070         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38071         var availWidth = Math.floor(w / vcount);
38072         var b = this.stripBody;
38073         if(b.getWidth() > w){
38074             var tabs = this.items;
38075             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38076             if(availWidth < this.minTabWidth){
38077                 /*if(!this.sleft){    // incomplete scrolling code
38078                     this.createScrollButtons();
38079                 }
38080                 this.showScroll();
38081                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38082             }
38083         }else{
38084             if(this.currentTabWidth < this.preferredTabWidth){
38085                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38086             }
38087         }
38088     },
38089
38090     /**
38091      * Returns the number of tabs in this TabPanel.
38092      * @return {Number}
38093      */
38094      getCount : function(){
38095          return this.items.length;
38096      },
38097
38098     /**
38099      * Resizes all the tabs to the passed width
38100      * @param {Number} The new width
38101      */
38102     setTabWidth : function(width){
38103         this.currentTabWidth = width;
38104         for(var i = 0, len = this.items.length; i < len; i++) {
38105                 if(!this.items[i].isHidden()) {
38106                 this.items[i].setWidth(width);
38107             }
38108         }
38109     },
38110
38111     /**
38112      * Destroys this TabPanel
38113      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38114      */
38115     destroy : function(removeEl){
38116         Roo.EventManager.removeResizeListener(this.onResize, this);
38117         for(var i = 0, len = this.items.length; i < len; i++){
38118             this.items[i].purgeListeners();
38119         }
38120         if(removeEl === true){
38121             this.el.update("");
38122             this.el.remove();
38123         }
38124     },
38125     
38126     createStrip : function(container)
38127     {
38128         var strip = document.createElement("nav");
38129         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38130         container.appendChild(strip);
38131         return strip;
38132     },
38133     
38134     createStripList : function(strip)
38135     {
38136         // div wrapper for retard IE
38137         // returns the "tr" element.
38138         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38139         //'<div class="x-tabs-strip-wrap">'+
38140           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38141           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38142         return strip.firstChild; //.firstChild.firstChild.firstChild;
38143     },
38144     createBody : function(container)
38145     {
38146         var body = document.createElement("div");
38147         Roo.id(body, "tab-body");
38148         //Roo.fly(body).addClass("x-tabs-body");
38149         Roo.fly(body).addClass("tab-content");
38150         container.appendChild(body);
38151         return body;
38152     },
38153     createItemBody :function(bodyEl, id){
38154         var body = Roo.getDom(id);
38155         if(!body){
38156             body = document.createElement("div");
38157             body.id = id;
38158         }
38159         //Roo.fly(body).addClass("x-tabs-item-body");
38160         Roo.fly(body).addClass("tab-pane");
38161          bodyEl.insertBefore(body, bodyEl.firstChild);
38162         return body;
38163     },
38164     /** @private */
38165     createStripElements :  function(stripEl, text, closable, tpl)
38166     {
38167         var td = document.createElement("li"); // was td..
38168         
38169         
38170         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38171         
38172         
38173         stripEl.appendChild(td);
38174         /*if(closable){
38175             td.className = "x-tabs-closable";
38176             if(!this.closeTpl){
38177                 this.closeTpl = new Roo.Template(
38178                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38179                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38180                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38181                 );
38182             }
38183             var el = this.closeTpl.overwrite(td, {"text": text});
38184             var close = el.getElementsByTagName("div")[0];
38185             var inner = el.getElementsByTagName("em")[0];
38186             return {"el": el, "close": close, "inner": inner};
38187         } else {
38188         */
38189         // not sure what this is..
38190 //            if(!this.tabTpl){
38191                 //this.tabTpl = new Roo.Template(
38192                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38193                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38194                 //);
38195 //                this.tabTpl = new Roo.Template(
38196 //                   '<a href="#">' +
38197 //                   '<span unselectable="on"' +
38198 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38199 //                            ' >{text}</span></a>'
38200 //                );
38201 //                
38202 //            }
38203
38204
38205             var template = tpl || this.tabTpl || false;
38206             
38207             if(!template){
38208                 
38209                 template = new Roo.Template(
38210                    '<a href="#">' +
38211                    '<span unselectable="on"' +
38212                             (this.disableTooltips ? '' : ' title="{text}"') +
38213                             ' >{text}</span></a>'
38214                 );
38215             }
38216             
38217             switch (typeof(template)) {
38218                 case 'object' :
38219                     break;
38220                 case 'string' :
38221                     template = new Roo.Template(template);
38222                     break;
38223                 default :
38224                     break;
38225             }
38226             
38227             var el = template.overwrite(td, {"text": text});
38228             
38229             var inner = el.getElementsByTagName("span")[0];
38230             
38231             return {"el": el, "inner": inner};
38232             
38233     }
38234         
38235     
38236 });
38237
38238 /**
38239  * @class Roo.TabPanelItem
38240  * @extends Roo.util.Observable
38241  * Represents an individual item (tab plus body) in a TabPanel.
38242  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38243  * @param {String} id The id of this TabPanelItem
38244  * @param {String} text The text for the tab of this TabPanelItem
38245  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38246  */
38247 Roo.bootstrap.panel.TabItem = function(config){
38248     /**
38249      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38250      * @type Roo.TabPanel
38251      */
38252     this.tabPanel = config.panel;
38253     /**
38254      * The id for this TabPanelItem
38255      * @type String
38256      */
38257     this.id = config.id;
38258     /** @private */
38259     this.disabled = false;
38260     /** @private */
38261     this.text = config.text;
38262     /** @private */
38263     this.loaded = false;
38264     this.closable = config.closable;
38265
38266     /**
38267      * The body element for this TabPanelItem.
38268      * @type Roo.Element
38269      */
38270     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38271     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38272     this.bodyEl.setStyle("display", "block");
38273     this.bodyEl.setStyle("zoom", "1");
38274     //this.hideAction();
38275
38276     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38277     /** @private */
38278     this.el = Roo.get(els.el);
38279     this.inner = Roo.get(els.inner, true);
38280     this.textEl = Roo.get(this.el.dom.firstChild, true);
38281     this.pnode = Roo.get(els.el.parentNode, true);
38282 //    this.el.on("mousedown", this.onTabMouseDown, this);
38283     this.el.on("click", this.onTabClick, this);
38284     /** @private */
38285     if(config.closable){
38286         var c = Roo.get(els.close, true);
38287         c.dom.title = this.closeText;
38288         c.addClassOnOver("close-over");
38289         c.on("click", this.closeClick, this);
38290      }
38291
38292     this.addEvents({
38293          /**
38294          * @event activate
38295          * Fires when this tab becomes the active tab.
38296          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38297          * @param {Roo.TabPanelItem} this
38298          */
38299         "activate": true,
38300         /**
38301          * @event beforeclose
38302          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38303          * @param {Roo.TabPanelItem} this
38304          * @param {Object} e Set cancel to true on this object to cancel the close.
38305          */
38306         "beforeclose": true,
38307         /**
38308          * @event close
38309          * Fires when this tab is closed.
38310          * @param {Roo.TabPanelItem} this
38311          */
38312          "close": true,
38313         /**
38314          * @event deactivate
38315          * Fires when this tab is no longer the active tab.
38316          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38317          * @param {Roo.TabPanelItem} this
38318          */
38319          "deactivate" : true
38320     });
38321     this.hidden = false;
38322
38323     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38324 };
38325
38326 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38327            {
38328     purgeListeners : function(){
38329        Roo.util.Observable.prototype.purgeListeners.call(this);
38330        this.el.removeAllListeners();
38331     },
38332     /**
38333      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38334      */
38335     show : function(){
38336         this.pnode.addClass("active");
38337         this.showAction();
38338         if(Roo.isOpera){
38339             this.tabPanel.stripWrap.repaint();
38340         }
38341         this.fireEvent("activate", this.tabPanel, this);
38342     },
38343
38344     /**
38345      * Returns true if this tab is the active tab.
38346      * @return {Boolean}
38347      */
38348     isActive : function(){
38349         return this.tabPanel.getActiveTab() == this;
38350     },
38351
38352     /**
38353      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38354      */
38355     hide : function(){
38356         this.pnode.removeClass("active");
38357         this.hideAction();
38358         this.fireEvent("deactivate", this.tabPanel, this);
38359     },
38360
38361     hideAction : function(){
38362         this.bodyEl.hide();
38363         this.bodyEl.setStyle("position", "absolute");
38364         this.bodyEl.setLeft("-20000px");
38365         this.bodyEl.setTop("-20000px");
38366     },
38367
38368     showAction : function(){
38369         this.bodyEl.setStyle("position", "relative");
38370         this.bodyEl.setTop("");
38371         this.bodyEl.setLeft("");
38372         this.bodyEl.show();
38373     },
38374
38375     /**
38376      * Set the tooltip for the tab.
38377      * @param {String} tooltip The tab's tooltip
38378      */
38379     setTooltip : function(text){
38380         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38381             this.textEl.dom.qtip = text;
38382             this.textEl.dom.removeAttribute('title');
38383         }else{
38384             this.textEl.dom.title = text;
38385         }
38386     },
38387
38388     onTabClick : function(e){
38389         e.preventDefault();
38390         this.tabPanel.activate(this.id);
38391     },
38392
38393     onTabMouseDown : function(e){
38394         e.preventDefault();
38395         this.tabPanel.activate(this.id);
38396     },
38397 /*
38398     getWidth : function(){
38399         return this.inner.getWidth();
38400     },
38401
38402     setWidth : function(width){
38403         var iwidth = width - this.pnode.getPadding("lr");
38404         this.inner.setWidth(iwidth);
38405         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38406         this.pnode.setWidth(width);
38407     },
38408 */
38409     /**
38410      * Show or hide the tab
38411      * @param {Boolean} hidden True to hide or false to show.
38412      */
38413     setHidden : function(hidden){
38414         this.hidden = hidden;
38415         this.pnode.setStyle("display", hidden ? "none" : "");
38416     },
38417
38418     /**
38419      * Returns true if this tab is "hidden"
38420      * @return {Boolean}
38421      */
38422     isHidden : function(){
38423         return this.hidden;
38424     },
38425
38426     /**
38427      * Returns the text for this tab
38428      * @return {String}
38429      */
38430     getText : function(){
38431         return this.text;
38432     },
38433     /*
38434     autoSize : function(){
38435         //this.el.beginMeasure();
38436         this.textEl.setWidth(1);
38437         /*
38438          *  #2804 [new] Tabs in Roojs
38439          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38440          */
38441         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38442         //this.el.endMeasure();
38443     //},
38444
38445     /**
38446      * Sets the text for the tab (Note: this also sets the tooltip text)
38447      * @param {String} text The tab's text and tooltip
38448      */
38449     setText : function(text){
38450         this.text = text;
38451         this.textEl.update(text);
38452         this.setTooltip(text);
38453         //if(!this.tabPanel.resizeTabs){
38454         //    this.autoSize();
38455         //}
38456     },
38457     /**
38458      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38459      */
38460     activate : function(){
38461         this.tabPanel.activate(this.id);
38462     },
38463
38464     /**
38465      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38466      */
38467     disable : function(){
38468         if(this.tabPanel.active != this){
38469             this.disabled = true;
38470             this.pnode.addClass("disabled");
38471         }
38472     },
38473
38474     /**
38475      * Enables this TabPanelItem if it was previously disabled.
38476      */
38477     enable : function(){
38478         this.disabled = false;
38479         this.pnode.removeClass("disabled");
38480     },
38481
38482     /**
38483      * Sets the content for this TabPanelItem.
38484      * @param {String} content The content
38485      * @param {Boolean} loadScripts true to look for and load scripts
38486      */
38487     setContent : function(content, loadScripts){
38488         this.bodyEl.update(content, loadScripts);
38489     },
38490
38491     /**
38492      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38493      * @return {Roo.UpdateManager} The UpdateManager
38494      */
38495     getUpdateManager : function(){
38496         return this.bodyEl.getUpdateManager();
38497     },
38498
38499     /**
38500      * Set a URL to be used to load the content for this TabPanelItem.
38501      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38502      * @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)
38503      * @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)
38504      * @return {Roo.UpdateManager} The UpdateManager
38505      */
38506     setUrl : function(url, params, loadOnce){
38507         if(this.refreshDelegate){
38508             this.un('activate', this.refreshDelegate);
38509         }
38510         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38511         this.on("activate", this.refreshDelegate);
38512         return this.bodyEl.getUpdateManager();
38513     },
38514
38515     /** @private */
38516     _handleRefresh : function(url, params, loadOnce){
38517         if(!loadOnce || !this.loaded){
38518             var updater = this.bodyEl.getUpdateManager();
38519             updater.update(url, params, this._setLoaded.createDelegate(this));
38520         }
38521     },
38522
38523     /**
38524      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38525      *   Will fail silently if the setUrl method has not been called.
38526      *   This does not activate the panel, just updates its content.
38527      */
38528     refresh : function(){
38529         if(this.refreshDelegate){
38530            this.loaded = false;
38531            this.refreshDelegate();
38532         }
38533     },
38534
38535     /** @private */
38536     _setLoaded : function(){
38537         this.loaded = true;
38538     },
38539
38540     /** @private */
38541     closeClick : function(e){
38542         var o = {};
38543         e.stopEvent();
38544         this.fireEvent("beforeclose", this, o);
38545         if(o.cancel !== true){
38546             this.tabPanel.removeTab(this.id);
38547         }
38548     },
38549     /**
38550      * The text displayed in the tooltip for the close icon.
38551      * @type String
38552      */
38553     closeText : "Close this tab"
38554 });
38555 /**
38556 *    This script refer to:
38557 *    Title: International Telephone Input
38558 *    Author: Jack O'Connor
38559 *    Code version:  v12.1.12
38560 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38561 **/
38562
38563 Roo.bootstrap.PhoneInputData = function() {
38564     var d = [
38565       [
38566         "Afghanistan (‫افغانستان‬‎)",
38567         "af",
38568         "93"
38569       ],
38570       [
38571         "Albania (Shqipëri)",
38572         "al",
38573         "355"
38574       ],
38575       [
38576         "Algeria (‫الجزائر‬‎)",
38577         "dz",
38578         "213"
38579       ],
38580       [
38581         "American Samoa",
38582         "as",
38583         "1684"
38584       ],
38585       [
38586         "Andorra",
38587         "ad",
38588         "376"
38589       ],
38590       [
38591         "Angola",
38592         "ao",
38593         "244"
38594       ],
38595       [
38596         "Anguilla",
38597         "ai",
38598         "1264"
38599       ],
38600       [
38601         "Antigua and Barbuda",
38602         "ag",
38603         "1268"
38604       ],
38605       [
38606         "Argentina",
38607         "ar",
38608         "54"
38609       ],
38610       [
38611         "Armenia (Հայաստան)",
38612         "am",
38613         "374"
38614       ],
38615       [
38616         "Aruba",
38617         "aw",
38618         "297"
38619       ],
38620       [
38621         "Australia",
38622         "au",
38623         "61",
38624         0
38625       ],
38626       [
38627         "Austria (Österreich)",
38628         "at",
38629         "43"
38630       ],
38631       [
38632         "Azerbaijan (Azərbaycan)",
38633         "az",
38634         "994"
38635       ],
38636       [
38637         "Bahamas",
38638         "bs",
38639         "1242"
38640       ],
38641       [
38642         "Bahrain (‫البحرين‬‎)",
38643         "bh",
38644         "973"
38645       ],
38646       [
38647         "Bangladesh (বাংলাদেশ)",
38648         "bd",
38649         "880"
38650       ],
38651       [
38652         "Barbados",
38653         "bb",
38654         "1246"
38655       ],
38656       [
38657         "Belarus (Беларусь)",
38658         "by",
38659         "375"
38660       ],
38661       [
38662         "Belgium (België)",
38663         "be",
38664         "32"
38665       ],
38666       [
38667         "Belize",
38668         "bz",
38669         "501"
38670       ],
38671       [
38672         "Benin (Bénin)",
38673         "bj",
38674         "229"
38675       ],
38676       [
38677         "Bermuda",
38678         "bm",
38679         "1441"
38680       ],
38681       [
38682         "Bhutan (འབྲུག)",
38683         "bt",
38684         "975"
38685       ],
38686       [
38687         "Bolivia",
38688         "bo",
38689         "591"
38690       ],
38691       [
38692         "Bosnia and Herzegovina (Босна и Херцеговина)",
38693         "ba",
38694         "387"
38695       ],
38696       [
38697         "Botswana",
38698         "bw",
38699         "267"
38700       ],
38701       [
38702         "Brazil (Brasil)",
38703         "br",
38704         "55"
38705       ],
38706       [
38707         "British Indian Ocean Territory",
38708         "io",
38709         "246"
38710       ],
38711       [
38712         "British Virgin Islands",
38713         "vg",
38714         "1284"
38715       ],
38716       [
38717         "Brunei",
38718         "bn",
38719         "673"
38720       ],
38721       [
38722         "Bulgaria (България)",
38723         "bg",
38724         "359"
38725       ],
38726       [
38727         "Burkina Faso",
38728         "bf",
38729         "226"
38730       ],
38731       [
38732         "Burundi (Uburundi)",
38733         "bi",
38734         "257"
38735       ],
38736       [
38737         "Cambodia (កម្ពុជា)",
38738         "kh",
38739         "855"
38740       ],
38741       [
38742         "Cameroon (Cameroun)",
38743         "cm",
38744         "237"
38745       ],
38746       [
38747         "Canada",
38748         "ca",
38749         "1",
38750         1,
38751         ["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"]
38752       ],
38753       [
38754         "Cape Verde (Kabu Verdi)",
38755         "cv",
38756         "238"
38757       ],
38758       [
38759         "Caribbean Netherlands",
38760         "bq",
38761         "599",
38762         1
38763       ],
38764       [
38765         "Cayman Islands",
38766         "ky",
38767         "1345"
38768       ],
38769       [
38770         "Central African Republic (République centrafricaine)",
38771         "cf",
38772         "236"
38773       ],
38774       [
38775         "Chad (Tchad)",
38776         "td",
38777         "235"
38778       ],
38779       [
38780         "Chile",
38781         "cl",
38782         "56"
38783       ],
38784       [
38785         "China (中国)",
38786         "cn",
38787         "86"
38788       ],
38789       [
38790         "Christmas Island",
38791         "cx",
38792         "61",
38793         2
38794       ],
38795       [
38796         "Cocos (Keeling) Islands",
38797         "cc",
38798         "61",
38799         1
38800       ],
38801       [
38802         "Colombia",
38803         "co",
38804         "57"
38805       ],
38806       [
38807         "Comoros (‫جزر القمر‬‎)",
38808         "km",
38809         "269"
38810       ],
38811       [
38812         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38813         "cd",
38814         "243"
38815       ],
38816       [
38817         "Congo (Republic) (Congo-Brazzaville)",
38818         "cg",
38819         "242"
38820       ],
38821       [
38822         "Cook Islands",
38823         "ck",
38824         "682"
38825       ],
38826       [
38827         "Costa Rica",
38828         "cr",
38829         "506"
38830       ],
38831       [
38832         "Côte d’Ivoire",
38833         "ci",
38834         "225"
38835       ],
38836       [
38837         "Croatia (Hrvatska)",
38838         "hr",
38839         "385"
38840       ],
38841       [
38842         "Cuba",
38843         "cu",
38844         "53"
38845       ],
38846       [
38847         "Curaçao",
38848         "cw",
38849         "599",
38850         0
38851       ],
38852       [
38853         "Cyprus (Κύπρος)",
38854         "cy",
38855         "357"
38856       ],
38857       [
38858         "Czech Republic (Česká republika)",
38859         "cz",
38860         "420"
38861       ],
38862       [
38863         "Denmark (Danmark)",
38864         "dk",
38865         "45"
38866       ],
38867       [
38868         "Djibouti",
38869         "dj",
38870         "253"
38871       ],
38872       [
38873         "Dominica",
38874         "dm",
38875         "1767"
38876       ],
38877       [
38878         "Dominican Republic (República Dominicana)",
38879         "do",
38880         "1",
38881         2,
38882         ["809", "829", "849"]
38883       ],
38884       [
38885         "Ecuador",
38886         "ec",
38887         "593"
38888       ],
38889       [
38890         "Egypt (‫مصر‬‎)",
38891         "eg",
38892         "20"
38893       ],
38894       [
38895         "El Salvador",
38896         "sv",
38897         "503"
38898       ],
38899       [
38900         "Equatorial Guinea (Guinea Ecuatorial)",
38901         "gq",
38902         "240"
38903       ],
38904       [
38905         "Eritrea",
38906         "er",
38907         "291"
38908       ],
38909       [
38910         "Estonia (Eesti)",
38911         "ee",
38912         "372"
38913       ],
38914       [
38915         "Ethiopia",
38916         "et",
38917         "251"
38918       ],
38919       [
38920         "Falkland Islands (Islas Malvinas)",
38921         "fk",
38922         "500"
38923       ],
38924       [
38925         "Faroe Islands (Føroyar)",
38926         "fo",
38927         "298"
38928       ],
38929       [
38930         "Fiji",
38931         "fj",
38932         "679"
38933       ],
38934       [
38935         "Finland (Suomi)",
38936         "fi",
38937         "358",
38938         0
38939       ],
38940       [
38941         "France",
38942         "fr",
38943         "33"
38944       ],
38945       [
38946         "French Guiana (Guyane française)",
38947         "gf",
38948         "594"
38949       ],
38950       [
38951         "French Polynesia (Polynésie française)",
38952         "pf",
38953         "689"
38954       ],
38955       [
38956         "Gabon",
38957         "ga",
38958         "241"
38959       ],
38960       [
38961         "Gambia",
38962         "gm",
38963         "220"
38964       ],
38965       [
38966         "Georgia (საქართველო)",
38967         "ge",
38968         "995"
38969       ],
38970       [
38971         "Germany (Deutschland)",
38972         "de",
38973         "49"
38974       ],
38975       [
38976         "Ghana (Gaana)",
38977         "gh",
38978         "233"
38979       ],
38980       [
38981         "Gibraltar",
38982         "gi",
38983         "350"
38984       ],
38985       [
38986         "Greece (Ελλάδα)",
38987         "gr",
38988         "30"
38989       ],
38990       [
38991         "Greenland (Kalaallit Nunaat)",
38992         "gl",
38993         "299"
38994       ],
38995       [
38996         "Grenada",
38997         "gd",
38998         "1473"
38999       ],
39000       [
39001         "Guadeloupe",
39002         "gp",
39003         "590",
39004         0
39005       ],
39006       [
39007         "Guam",
39008         "gu",
39009         "1671"
39010       ],
39011       [
39012         "Guatemala",
39013         "gt",
39014         "502"
39015       ],
39016       [
39017         "Guernsey",
39018         "gg",
39019         "44",
39020         1
39021       ],
39022       [
39023         "Guinea (Guinée)",
39024         "gn",
39025         "224"
39026       ],
39027       [
39028         "Guinea-Bissau (Guiné Bissau)",
39029         "gw",
39030         "245"
39031       ],
39032       [
39033         "Guyana",
39034         "gy",
39035         "592"
39036       ],
39037       [
39038         "Haiti",
39039         "ht",
39040         "509"
39041       ],
39042       [
39043         "Honduras",
39044         "hn",
39045         "504"
39046       ],
39047       [
39048         "Hong Kong (香港)",
39049         "hk",
39050         "852"
39051       ],
39052       [
39053         "Hungary (Magyarország)",
39054         "hu",
39055         "36"
39056       ],
39057       [
39058         "Iceland (Ísland)",
39059         "is",
39060         "354"
39061       ],
39062       [
39063         "India (भारत)",
39064         "in",
39065         "91"
39066       ],
39067       [
39068         "Indonesia",
39069         "id",
39070         "62"
39071       ],
39072       [
39073         "Iran (‫ایران‬‎)",
39074         "ir",
39075         "98"
39076       ],
39077       [
39078         "Iraq (‫العراق‬‎)",
39079         "iq",
39080         "964"
39081       ],
39082       [
39083         "Ireland",
39084         "ie",
39085         "353"
39086       ],
39087       [
39088         "Isle of Man",
39089         "im",
39090         "44",
39091         2
39092       ],
39093       [
39094         "Israel (‫ישראל‬‎)",
39095         "il",
39096         "972"
39097       ],
39098       [
39099         "Italy (Italia)",
39100         "it",
39101         "39",
39102         0
39103       ],
39104       [
39105         "Jamaica",
39106         "jm",
39107         "1876"
39108       ],
39109       [
39110         "Japan (日本)",
39111         "jp",
39112         "81"
39113       ],
39114       [
39115         "Jersey",
39116         "je",
39117         "44",
39118         3
39119       ],
39120       [
39121         "Jordan (‫الأردن‬‎)",
39122         "jo",
39123         "962"
39124       ],
39125       [
39126         "Kazakhstan (Казахстан)",
39127         "kz",
39128         "7",
39129         1
39130       ],
39131       [
39132         "Kenya",
39133         "ke",
39134         "254"
39135       ],
39136       [
39137         "Kiribati",
39138         "ki",
39139         "686"
39140       ],
39141       [
39142         "Kosovo",
39143         "xk",
39144         "383"
39145       ],
39146       [
39147         "Kuwait (‫الكويت‬‎)",
39148         "kw",
39149         "965"
39150       ],
39151       [
39152         "Kyrgyzstan (Кыргызстан)",
39153         "kg",
39154         "996"
39155       ],
39156       [
39157         "Laos (ລາວ)",
39158         "la",
39159         "856"
39160       ],
39161       [
39162         "Latvia (Latvija)",
39163         "lv",
39164         "371"
39165       ],
39166       [
39167         "Lebanon (‫لبنان‬‎)",
39168         "lb",
39169         "961"
39170       ],
39171       [
39172         "Lesotho",
39173         "ls",
39174         "266"
39175       ],
39176       [
39177         "Liberia",
39178         "lr",
39179         "231"
39180       ],
39181       [
39182         "Libya (‫ليبيا‬‎)",
39183         "ly",
39184         "218"
39185       ],
39186       [
39187         "Liechtenstein",
39188         "li",
39189         "423"
39190       ],
39191       [
39192         "Lithuania (Lietuva)",
39193         "lt",
39194         "370"
39195       ],
39196       [
39197         "Luxembourg",
39198         "lu",
39199         "352"
39200       ],
39201       [
39202         "Macau (澳門)",
39203         "mo",
39204         "853"
39205       ],
39206       [
39207         "Macedonia (FYROM) (Македонија)",
39208         "mk",
39209         "389"
39210       ],
39211       [
39212         "Madagascar (Madagasikara)",
39213         "mg",
39214         "261"
39215       ],
39216       [
39217         "Malawi",
39218         "mw",
39219         "265"
39220       ],
39221       [
39222         "Malaysia",
39223         "my",
39224         "60"
39225       ],
39226       [
39227         "Maldives",
39228         "mv",
39229         "960"
39230       ],
39231       [
39232         "Mali",
39233         "ml",
39234         "223"
39235       ],
39236       [
39237         "Malta",
39238         "mt",
39239         "356"
39240       ],
39241       [
39242         "Marshall Islands",
39243         "mh",
39244         "692"
39245       ],
39246       [
39247         "Martinique",
39248         "mq",
39249         "596"
39250       ],
39251       [
39252         "Mauritania (‫موريتانيا‬‎)",
39253         "mr",
39254         "222"
39255       ],
39256       [
39257         "Mauritius (Moris)",
39258         "mu",
39259         "230"
39260       ],
39261       [
39262         "Mayotte",
39263         "yt",
39264         "262",
39265         1
39266       ],
39267       [
39268         "Mexico (México)",
39269         "mx",
39270         "52"
39271       ],
39272       [
39273         "Micronesia",
39274         "fm",
39275         "691"
39276       ],
39277       [
39278         "Moldova (Republica Moldova)",
39279         "md",
39280         "373"
39281       ],
39282       [
39283         "Monaco",
39284         "mc",
39285         "377"
39286       ],
39287       [
39288         "Mongolia (Монгол)",
39289         "mn",
39290         "976"
39291       ],
39292       [
39293         "Montenegro (Crna Gora)",
39294         "me",
39295         "382"
39296       ],
39297       [
39298         "Montserrat",
39299         "ms",
39300         "1664"
39301       ],
39302       [
39303         "Morocco (‫المغرب‬‎)",
39304         "ma",
39305         "212",
39306         0
39307       ],
39308       [
39309         "Mozambique (Moçambique)",
39310         "mz",
39311         "258"
39312       ],
39313       [
39314         "Myanmar (Burma) (မြန်မာ)",
39315         "mm",
39316         "95"
39317       ],
39318       [
39319         "Namibia (Namibië)",
39320         "na",
39321         "264"
39322       ],
39323       [
39324         "Nauru",
39325         "nr",
39326         "674"
39327       ],
39328       [
39329         "Nepal (नेपाल)",
39330         "np",
39331         "977"
39332       ],
39333       [
39334         "Netherlands (Nederland)",
39335         "nl",
39336         "31"
39337       ],
39338       [
39339         "New Caledonia (Nouvelle-Calédonie)",
39340         "nc",
39341         "687"
39342       ],
39343       [
39344         "New Zealand",
39345         "nz",
39346         "64"
39347       ],
39348       [
39349         "Nicaragua",
39350         "ni",
39351         "505"
39352       ],
39353       [
39354         "Niger (Nijar)",
39355         "ne",
39356         "227"
39357       ],
39358       [
39359         "Nigeria",
39360         "ng",
39361         "234"
39362       ],
39363       [
39364         "Niue",
39365         "nu",
39366         "683"
39367       ],
39368       [
39369         "Norfolk Island",
39370         "nf",
39371         "672"
39372       ],
39373       [
39374         "North Korea (조선 민주주의 인민 공화국)",
39375         "kp",
39376         "850"
39377       ],
39378       [
39379         "Northern Mariana Islands",
39380         "mp",
39381         "1670"
39382       ],
39383       [
39384         "Norway (Norge)",
39385         "no",
39386         "47",
39387         0
39388       ],
39389       [
39390         "Oman (‫عُمان‬‎)",
39391         "om",
39392         "968"
39393       ],
39394       [
39395         "Pakistan (‫پاکستان‬‎)",
39396         "pk",
39397         "92"
39398       ],
39399       [
39400         "Palau",
39401         "pw",
39402         "680"
39403       ],
39404       [
39405         "Palestine (‫فلسطين‬‎)",
39406         "ps",
39407         "970"
39408       ],
39409       [
39410         "Panama (Panamá)",
39411         "pa",
39412         "507"
39413       ],
39414       [
39415         "Papua New Guinea",
39416         "pg",
39417         "675"
39418       ],
39419       [
39420         "Paraguay",
39421         "py",
39422         "595"
39423       ],
39424       [
39425         "Peru (Perú)",
39426         "pe",
39427         "51"
39428       ],
39429       [
39430         "Philippines",
39431         "ph",
39432         "63"
39433       ],
39434       [
39435         "Poland (Polska)",
39436         "pl",
39437         "48"
39438       ],
39439       [
39440         "Portugal",
39441         "pt",
39442         "351"
39443       ],
39444       [
39445         "Puerto Rico",
39446         "pr",
39447         "1",
39448         3,
39449         ["787", "939"]
39450       ],
39451       [
39452         "Qatar (‫قطر‬‎)",
39453         "qa",
39454         "974"
39455       ],
39456       [
39457         "Réunion (La Réunion)",
39458         "re",
39459         "262",
39460         0
39461       ],
39462       [
39463         "Romania (România)",
39464         "ro",
39465         "40"
39466       ],
39467       [
39468         "Russia (Россия)",
39469         "ru",
39470         "7",
39471         0
39472       ],
39473       [
39474         "Rwanda",
39475         "rw",
39476         "250"
39477       ],
39478       [
39479         "Saint Barthélemy",
39480         "bl",
39481         "590",
39482         1
39483       ],
39484       [
39485         "Saint Helena",
39486         "sh",
39487         "290"
39488       ],
39489       [
39490         "Saint Kitts and Nevis",
39491         "kn",
39492         "1869"
39493       ],
39494       [
39495         "Saint Lucia",
39496         "lc",
39497         "1758"
39498       ],
39499       [
39500         "Saint Martin (Saint-Martin (partie française))",
39501         "mf",
39502         "590",
39503         2
39504       ],
39505       [
39506         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39507         "pm",
39508         "508"
39509       ],
39510       [
39511         "Saint Vincent and the Grenadines",
39512         "vc",
39513         "1784"
39514       ],
39515       [
39516         "Samoa",
39517         "ws",
39518         "685"
39519       ],
39520       [
39521         "San Marino",
39522         "sm",
39523         "378"
39524       ],
39525       [
39526         "São Tomé and Príncipe (São Tomé e Príncipe)",
39527         "st",
39528         "239"
39529       ],
39530       [
39531         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39532         "sa",
39533         "966"
39534       ],
39535       [
39536         "Senegal (Sénégal)",
39537         "sn",
39538         "221"
39539       ],
39540       [
39541         "Serbia (Србија)",
39542         "rs",
39543         "381"
39544       ],
39545       [
39546         "Seychelles",
39547         "sc",
39548         "248"
39549       ],
39550       [
39551         "Sierra Leone",
39552         "sl",
39553         "232"
39554       ],
39555       [
39556         "Singapore",
39557         "sg",
39558         "65"
39559       ],
39560       [
39561         "Sint Maarten",
39562         "sx",
39563         "1721"
39564       ],
39565       [
39566         "Slovakia (Slovensko)",
39567         "sk",
39568         "421"
39569       ],
39570       [
39571         "Slovenia (Slovenija)",
39572         "si",
39573         "386"
39574       ],
39575       [
39576         "Solomon Islands",
39577         "sb",
39578         "677"
39579       ],
39580       [
39581         "Somalia (Soomaaliya)",
39582         "so",
39583         "252"
39584       ],
39585       [
39586         "South Africa",
39587         "za",
39588         "27"
39589       ],
39590       [
39591         "South Korea (대한민국)",
39592         "kr",
39593         "82"
39594       ],
39595       [
39596         "South Sudan (‫جنوب السودان‬‎)",
39597         "ss",
39598         "211"
39599       ],
39600       [
39601         "Spain (España)",
39602         "es",
39603         "34"
39604       ],
39605       [
39606         "Sri Lanka (ශ්‍රී ලංකාව)",
39607         "lk",
39608         "94"
39609       ],
39610       [
39611         "Sudan (‫السودان‬‎)",
39612         "sd",
39613         "249"
39614       ],
39615       [
39616         "Suriname",
39617         "sr",
39618         "597"
39619       ],
39620       [
39621         "Svalbard and Jan Mayen",
39622         "sj",
39623         "47",
39624         1
39625       ],
39626       [
39627         "Swaziland",
39628         "sz",
39629         "268"
39630       ],
39631       [
39632         "Sweden (Sverige)",
39633         "se",
39634         "46"
39635       ],
39636       [
39637         "Switzerland (Schweiz)",
39638         "ch",
39639         "41"
39640       ],
39641       [
39642         "Syria (‫سوريا‬‎)",
39643         "sy",
39644         "963"
39645       ],
39646       [
39647         "Taiwan (台灣)",
39648         "tw",
39649         "886"
39650       ],
39651       [
39652         "Tajikistan",
39653         "tj",
39654         "992"
39655       ],
39656       [
39657         "Tanzania",
39658         "tz",
39659         "255"
39660       ],
39661       [
39662         "Thailand (ไทย)",
39663         "th",
39664         "66"
39665       ],
39666       [
39667         "Timor-Leste",
39668         "tl",
39669         "670"
39670       ],
39671       [
39672         "Togo",
39673         "tg",
39674         "228"
39675       ],
39676       [
39677         "Tokelau",
39678         "tk",
39679         "690"
39680       ],
39681       [
39682         "Tonga",
39683         "to",
39684         "676"
39685       ],
39686       [
39687         "Trinidad and Tobago",
39688         "tt",
39689         "1868"
39690       ],
39691       [
39692         "Tunisia (‫تونس‬‎)",
39693         "tn",
39694         "216"
39695       ],
39696       [
39697         "Turkey (Türkiye)",
39698         "tr",
39699         "90"
39700       ],
39701       [
39702         "Turkmenistan",
39703         "tm",
39704         "993"
39705       ],
39706       [
39707         "Turks and Caicos Islands",
39708         "tc",
39709         "1649"
39710       ],
39711       [
39712         "Tuvalu",
39713         "tv",
39714         "688"
39715       ],
39716       [
39717         "U.S. Virgin Islands",
39718         "vi",
39719         "1340"
39720       ],
39721       [
39722         "Uganda",
39723         "ug",
39724         "256"
39725       ],
39726       [
39727         "Ukraine (Україна)",
39728         "ua",
39729         "380"
39730       ],
39731       [
39732         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39733         "ae",
39734         "971"
39735       ],
39736       [
39737         "United Kingdom",
39738         "gb",
39739         "44",
39740         0
39741       ],
39742       [
39743         "United States",
39744         "us",
39745         "1",
39746         0
39747       ],
39748       [
39749         "Uruguay",
39750         "uy",
39751         "598"
39752       ],
39753       [
39754         "Uzbekistan (Oʻzbekiston)",
39755         "uz",
39756         "998"
39757       ],
39758       [
39759         "Vanuatu",
39760         "vu",
39761         "678"
39762       ],
39763       [
39764         "Vatican City (Città del Vaticano)",
39765         "va",
39766         "39",
39767         1
39768       ],
39769       [
39770         "Venezuela",
39771         "ve",
39772         "58"
39773       ],
39774       [
39775         "Vietnam (Việt Nam)",
39776         "vn",
39777         "84"
39778       ],
39779       [
39780         "Wallis and Futuna (Wallis-et-Futuna)",
39781         "wf",
39782         "681"
39783       ],
39784       [
39785         "Western Sahara (‫الصحراء الغربية‬‎)",
39786         "eh",
39787         "212",
39788         1
39789       ],
39790       [
39791         "Yemen (‫اليمن‬‎)",
39792         "ye",
39793         "967"
39794       ],
39795       [
39796         "Zambia",
39797         "zm",
39798         "260"
39799       ],
39800       [
39801         "Zimbabwe",
39802         "zw",
39803         "263"
39804       ],
39805       [
39806         "Åland Islands",
39807         "ax",
39808         "358",
39809         1
39810       ]
39811   ];
39812   
39813   return d;
39814 }/**
39815 *    This script refer to:
39816 *    Title: International Telephone Input
39817 *    Author: Jack O'Connor
39818 *    Code version:  v12.1.12
39819 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39820 **/
39821
39822 /**
39823  * @class Roo.bootstrap.PhoneInput
39824  * @extends Roo.bootstrap.TriggerField
39825  * An input with International dial-code selection
39826  
39827  * @cfg {String} defaultDialCode default '+852'
39828  * @cfg {Array} preferedCountries default []
39829   
39830  * @constructor
39831  * Create a new PhoneInput.
39832  * @param {Object} config Configuration options
39833  */
39834
39835 Roo.bootstrap.PhoneInput = function(config) {
39836     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39837 };
39838
39839 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39840         
39841         listWidth: undefined,
39842         
39843         selectedClass: 'active',
39844         
39845         invalidClass : "has-warning",
39846         
39847         validClass: 'has-success',
39848         
39849         allowed: '0123456789',
39850         
39851         /**
39852          * @cfg {String} defaultDialCode The default dial code when initializing the input
39853          */
39854         defaultDialCode: '+852',
39855         
39856         /**
39857          * @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
39858          */
39859         preferedCountries: false,
39860         
39861         getAutoCreate : function()
39862         {
39863             var data = Roo.bootstrap.PhoneInputData();
39864             var align = this.labelAlign || this.parentLabelAlign();
39865             var id = Roo.id();
39866             
39867             this.allCountries = [];
39868             this.dialCodeMapping = [];
39869             
39870             for (var i = 0; i < data.length; i++) {
39871               var c = data[i];
39872               this.allCountries[i] = {
39873                 name: c[0],
39874                 iso2: c[1],
39875                 dialCode: c[2],
39876                 priority: c[3] || 0,
39877                 areaCodes: c[4] || null
39878               };
39879               this.dialCodeMapping[c[2]] = {
39880                   name: c[0],
39881                   iso2: c[1],
39882                   priority: c[3] || 0,
39883                   areaCodes: c[4] || null
39884               };
39885             }
39886             
39887             var cfg = {
39888                 cls: 'form-group',
39889                 cn: []
39890             };
39891             
39892             var input =  {
39893                 tag: 'input',
39894                 id : id,
39895                 cls : 'form-control tel-input',
39896                 autocomplete: 'new-password'
39897             };
39898             
39899             var hiddenInput = {
39900                 tag: 'input',
39901                 type: 'hidden',
39902                 cls: 'hidden-tel-input'
39903             };
39904             
39905             if (this.name) {
39906                 hiddenInput.name = this.name;
39907             }
39908             
39909             if (this.disabled) {
39910                 input.disabled = true;
39911             }
39912             
39913             var flag_container = {
39914                 tag: 'div',
39915                 cls: 'flag-box',
39916                 cn: [
39917                     {
39918                         tag: 'div',
39919                         cls: 'flag'
39920                     },
39921                     {
39922                         tag: 'div',
39923                         cls: 'caret'
39924                     }
39925                 ]
39926             };
39927             
39928             var box = {
39929                 tag: 'div',
39930                 cls: this.hasFeedback ? 'has-feedback' : '',
39931                 cn: [
39932                     hiddenInput,
39933                     input,
39934                     {
39935                         tag: 'input',
39936                         cls: 'dial-code-holder',
39937                         disabled: true
39938                     }
39939                 ]
39940             };
39941             
39942             var container = {
39943                 cls: 'roo-select2-container input-group',
39944                 cn: [
39945                     flag_container,
39946                     box
39947                 ]
39948             };
39949             
39950             if (this.fieldLabel.length) {
39951                 var indicator = {
39952                     tag: 'i',
39953                     tooltip: 'This field is required'
39954                 };
39955                 
39956                 var label = {
39957                     tag: 'label',
39958                     'for':  id,
39959                     cls: 'control-label',
39960                     cn: []
39961                 };
39962                 
39963                 var label_text = {
39964                     tag: 'span',
39965                     html: this.fieldLabel
39966                 };
39967                 
39968                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39969                 label.cn = [
39970                     indicator,
39971                     label_text
39972                 ];
39973                 
39974                 if(this.indicatorpos == 'right') {
39975                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39976                     label.cn = [
39977                         label_text,
39978                         indicator
39979                     ];
39980                 }
39981                 
39982                 if(align == 'left') {
39983                     container = {
39984                         tag: 'div',
39985                         cn: [
39986                             container
39987                         ]
39988                     };
39989                     
39990                     if(this.labelWidth > 12){
39991                         label.style = "width: " + this.labelWidth + 'px';
39992                     }
39993                     if(this.labelWidth < 13 && this.labelmd == 0){
39994                         this.labelmd = this.labelWidth;
39995                     }
39996                     if(this.labellg > 0){
39997                         label.cls += ' col-lg-' + this.labellg;
39998                         input.cls += ' col-lg-' + (12 - this.labellg);
39999                     }
40000                     if(this.labelmd > 0){
40001                         label.cls += ' col-md-' + this.labelmd;
40002                         container.cls += ' col-md-' + (12 - this.labelmd);
40003                     }
40004                     if(this.labelsm > 0){
40005                         label.cls += ' col-sm-' + this.labelsm;
40006                         container.cls += ' col-sm-' + (12 - this.labelsm);
40007                     }
40008                     if(this.labelxs > 0){
40009                         label.cls += ' col-xs-' + this.labelxs;
40010                         container.cls += ' col-xs-' + (12 - this.labelxs);
40011                     }
40012                 }
40013             }
40014             
40015             cfg.cn = [
40016                 label,
40017                 container
40018             ];
40019             
40020             var settings = this;
40021             
40022             ['xs','sm','md','lg'].map(function(size){
40023                 if (settings[size]) {
40024                     cfg.cls += ' col-' + size + '-' + settings[size];
40025                 }
40026             });
40027             
40028             this.store = new Roo.data.Store({
40029                 proxy : new Roo.data.MemoryProxy({}),
40030                 reader : new Roo.data.JsonReader({
40031                     fields : [
40032                         {
40033                             'name' : 'name',
40034                             'type' : 'string'
40035                         },
40036                         {
40037                             'name' : 'iso2',
40038                             'type' : 'string'
40039                         },
40040                         {
40041                             'name' : 'dialCode',
40042                             'type' : 'string'
40043                         },
40044                         {
40045                             'name' : 'priority',
40046                             'type' : 'string'
40047                         },
40048                         {
40049                             'name' : 'areaCodes',
40050                             'type' : 'string'
40051                         }
40052                     ]
40053                 })
40054             });
40055             
40056             if(!this.preferedCountries) {
40057                 this.preferedCountries = [
40058                     'hk',
40059                     'gb',
40060                     'us'
40061                 ];
40062             }
40063             
40064             var p = this.preferedCountries.reverse();
40065             
40066             if(p) {
40067                 for (var i = 0; i < p.length; i++) {
40068                     for (var j = 0; j < this.allCountries.length; j++) {
40069                         if(this.allCountries[j].iso2 == p[i]) {
40070                             var t = this.allCountries[j];
40071                             this.allCountries.splice(j,1);
40072                             this.allCountries.unshift(t);
40073                         }
40074                     } 
40075                 }
40076             }
40077             
40078             this.store.proxy.data = {
40079                 success: true,
40080                 data: this.allCountries
40081             };
40082             
40083             return cfg;
40084         },
40085         
40086         initEvents : function()
40087         {
40088             this.createList();
40089             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40090             
40091             this.indicator = this.indicatorEl();
40092             this.flag = this.flagEl();
40093             this.dialCodeHolder = this.dialCodeHolderEl();
40094             
40095             this.trigger = this.el.select('div.flag-box',true).first();
40096             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40097             
40098             var _this = this;
40099             
40100             (function(){
40101                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40102                 _this.list.setWidth(lw);
40103             }).defer(100);
40104             
40105             this.list.on('mouseover', this.onViewOver, this);
40106             this.list.on('mousemove', this.onViewMove, this);
40107             this.inputEl().on("keyup", this.onKeyUp, this);
40108             
40109             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40110
40111             this.view = new Roo.View(this.list, this.tpl, {
40112                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40113             });
40114             
40115             this.view.on('click', this.onViewClick, this);
40116             this.setValue(this.defaultDialCode);
40117         },
40118         
40119         onTriggerClick : function(e)
40120         {
40121             Roo.log('trigger click');
40122             if(this.disabled){
40123                 return;
40124             }
40125             
40126             if(this.isExpanded()){
40127                 this.collapse();
40128                 this.hasFocus = false;
40129             }else {
40130                 this.store.load({});
40131                 this.hasFocus = true;
40132                 this.expand();
40133             }
40134         },
40135         
40136         isExpanded : function()
40137         {
40138             return this.list.isVisible();
40139         },
40140         
40141         collapse : function()
40142         {
40143             if(!this.isExpanded()){
40144                 return;
40145             }
40146             this.list.hide();
40147             Roo.get(document).un('mousedown', this.collapseIf, this);
40148             Roo.get(document).un('mousewheel', this.collapseIf, this);
40149             this.fireEvent('collapse', this);
40150             this.validate();
40151         },
40152         
40153         expand : function()
40154         {
40155             Roo.log('expand');
40156
40157             if(this.isExpanded() || !this.hasFocus){
40158                 return;
40159             }
40160             
40161             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40162             this.list.setWidth(lw);
40163             
40164             this.list.show();
40165             this.restrictHeight();
40166             
40167             Roo.get(document).on('mousedown', this.collapseIf, this);
40168             Roo.get(document).on('mousewheel', this.collapseIf, this);
40169             
40170             this.fireEvent('expand', this);
40171         },
40172         
40173         restrictHeight : function()
40174         {
40175             this.list.alignTo(this.inputEl(), this.listAlign);
40176             this.list.alignTo(this.inputEl(), this.listAlign);
40177         },
40178         
40179         onViewOver : function(e, t)
40180         {
40181             if(this.inKeyMode){
40182                 return;
40183             }
40184             var item = this.view.findItemFromChild(t);
40185             
40186             if(item){
40187                 var index = this.view.indexOf(item);
40188                 this.select(index, false);
40189             }
40190         },
40191
40192         // private
40193         onViewClick : function(view, doFocus, el, e)
40194         {
40195             var index = this.view.getSelectedIndexes()[0];
40196             
40197             var r = this.store.getAt(index);
40198             
40199             if(r){
40200                 this.onSelect(r, index);
40201             }
40202             if(doFocus !== false && !this.blockFocus){
40203                 this.inputEl().focus();
40204             }
40205         },
40206         
40207         onViewMove : function(e, t)
40208         {
40209             this.inKeyMode = false;
40210         },
40211         
40212         select : function(index, scrollIntoView)
40213         {
40214             this.selectedIndex = index;
40215             this.view.select(index);
40216             if(scrollIntoView !== false){
40217                 var el = this.view.getNode(index);
40218                 if(el){
40219                     this.list.scrollChildIntoView(el, false);
40220                 }
40221             }
40222         },
40223         
40224         createList : function()
40225         {
40226             this.list = Roo.get(document.body).createChild({
40227                 tag: 'ul',
40228                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40229                 style: 'display:none'
40230             });
40231             
40232             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40233         },
40234         
40235         collapseIf : function(e)
40236         {
40237             var in_combo  = e.within(this.el);
40238             var in_list =  e.within(this.list);
40239             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40240             
40241             if (in_combo || in_list || is_list) {
40242                 return;
40243             }
40244             this.collapse();
40245         },
40246         
40247         onSelect : function(record, index)
40248         {
40249             if(this.fireEvent('beforeselect', this, record, index) !== false){
40250                 
40251                 this.setFlagClass(record.data.iso2);
40252                 this.setDialCode(record.data.dialCode);
40253                 this.hasFocus = false;
40254                 this.collapse();
40255                 this.fireEvent('select', this, record, index);
40256             }
40257         },
40258         
40259         flagEl : function()
40260         {
40261             var flag = this.el.select('div.flag',true).first();
40262             if(!flag){
40263                 return false;
40264             }
40265             return flag;
40266         },
40267         
40268         dialCodeHolderEl : function()
40269         {
40270             var d = this.el.select('input.dial-code-holder',true).first();
40271             if(!d){
40272                 return false;
40273             }
40274             return d;
40275         },
40276         
40277         setDialCode : function(v)
40278         {
40279             this.dialCodeHolder.dom.value = '+'+v;
40280         },
40281         
40282         setFlagClass : function(n)
40283         {
40284             this.flag.dom.className = 'flag '+n;
40285         },
40286         
40287         getValue : function()
40288         {
40289             var v = this.inputEl().getValue();
40290             if(this.dialCodeHolder) {
40291                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40292             }
40293             return v;
40294         },
40295         
40296         setValue : function(v)
40297         {
40298             var d = this.getDialCode(v);
40299             
40300             //invalid dial code
40301             if(v.length == 0 || !d || d.length == 0) {
40302                 if(this.rendered){
40303                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40304                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40305                 }
40306                 return;
40307             }
40308             
40309             //valid dial code
40310             this.setFlagClass(this.dialCodeMapping[d].iso2);
40311             this.setDialCode(d);
40312             this.inputEl().dom.value = v.replace('+'+d,'');
40313             this.hiddenEl().dom.value = this.getValue();
40314             
40315             this.validate();
40316         },
40317         
40318         getDialCode : function(v)
40319         {
40320             v = v ||  '';
40321             
40322             if (v.length == 0) {
40323                 return this.dialCodeHolder.dom.value;
40324             }
40325             
40326             var dialCode = "";
40327             if (v.charAt(0) != "+") {
40328                 return false;
40329             }
40330             var numericChars = "";
40331             for (var i = 1; i < v.length; i++) {
40332               var c = v.charAt(i);
40333               if (!isNaN(c)) {
40334                 numericChars += c;
40335                 if (this.dialCodeMapping[numericChars]) {
40336                   dialCode = v.substr(1, i);
40337                 }
40338                 if (numericChars.length == 4) {
40339                   break;
40340                 }
40341               }
40342             }
40343             return dialCode;
40344         },
40345         
40346         reset : function()
40347         {
40348             this.setValue(this.defaultDialCode);
40349             this.validate();
40350         },
40351         
40352         hiddenEl : function()
40353         {
40354             return this.el.select('input.hidden-tel-input',true).first();
40355         },
40356         
40357         onKeyUp : function(e){
40358             
40359             var k = e.getKey();
40360             var c = e.getCharCode();
40361             
40362             if(
40363                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40364                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40365             ){
40366                 e.stopEvent();
40367             }
40368             
40369             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40370             //     return;
40371             // }
40372             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40373                 e.stopEvent();
40374             }
40375             
40376             this.setValue(this.getValue());
40377         }
40378         
40379 });
40380 /**
40381  * @class Roo.bootstrap.MoneyField
40382  * @extends Roo.bootstrap.ComboBox
40383  * Bootstrap MoneyField class
40384  * 
40385  * @constructor
40386  * Create a new MoneyField.
40387  * @param {Object} config Configuration options
40388  */
40389
40390 Roo.bootstrap.MoneyField = function(config) {
40391     
40392     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40393     
40394 };
40395
40396 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40397     
40398     /**
40399      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40400      */
40401     allowDecimals : true,
40402     /**
40403      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40404      */
40405     decimalSeparator : ".",
40406     /**
40407      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40408      */
40409     decimalPrecision : 0,
40410     /**
40411      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40412      */
40413     allowNegative : true,
40414     /**
40415      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40416      */
40417     allowZero: true,
40418     /**
40419      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40420      */
40421     minValue : Number.NEGATIVE_INFINITY,
40422     /**
40423      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40424      */
40425     maxValue : Number.MAX_VALUE,
40426     /**
40427      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40428      */
40429     minText : "The minimum value for this field is {0}",
40430     /**
40431      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40432      */
40433     maxText : "The maximum value for this field is {0}",
40434     /**
40435      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40436      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40437      */
40438     nanText : "{0} is not a valid number",
40439     /**
40440      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40441      */
40442     castInt : true,
40443     /**
40444      * @cfg {String} defaults currency of the MoneyField
40445      * value should be in lkey
40446      */
40447     defaultCurrency : false,
40448     /**
40449      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40450      */
40451     thousandsDelimiter : false,
40452     
40453     
40454     inputlg : 9,
40455     inputmd : 9,
40456     inputsm : 9,
40457     inputxs : 6,
40458     
40459     store : false,
40460     
40461     getAutoCreate : function()
40462     {
40463         var align = this.labelAlign || this.parentLabelAlign();
40464         
40465         var id = Roo.id();
40466
40467         var cfg = {
40468             cls: 'form-group',
40469             cn: []
40470         };
40471
40472         var input =  {
40473             tag: 'input',
40474             id : id,
40475             cls : 'form-control roo-money-amount-input',
40476             autocomplete: 'new-password'
40477         };
40478         
40479         var hiddenInput = {
40480             tag: 'input',
40481             type: 'hidden',
40482             id: Roo.id(),
40483             cls: 'hidden-number-input'
40484         };
40485         
40486         if (this.name) {
40487             hiddenInput.name = this.name;
40488         }
40489
40490         if (this.disabled) {
40491             input.disabled = true;
40492         }
40493
40494         var clg = 12 - this.inputlg;
40495         var cmd = 12 - this.inputmd;
40496         var csm = 12 - this.inputsm;
40497         var cxs = 12 - this.inputxs;
40498         
40499         var container = {
40500             tag : 'div',
40501             cls : 'row roo-money-field',
40502             cn : [
40503                 {
40504                     tag : 'div',
40505                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40506                     cn : [
40507                         {
40508                             tag : 'div',
40509                             cls: 'roo-select2-container input-group',
40510                             cn: [
40511                                 {
40512                                     tag : 'input',
40513                                     cls : 'form-control roo-money-currency-input',
40514                                     autocomplete: 'new-password',
40515                                     readOnly : 1,
40516                                     name : this.currencyName
40517                                 },
40518                                 {
40519                                     tag :'span',
40520                                     cls : 'input-group-addon',
40521                                     cn : [
40522                                         {
40523                                             tag: 'span',
40524                                             cls: 'caret'
40525                                         }
40526                                     ]
40527                                 }
40528                             ]
40529                         }
40530                     ]
40531                 },
40532                 {
40533                     tag : 'div',
40534                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40535                     cn : [
40536                         {
40537                             tag: 'div',
40538                             cls: this.hasFeedback ? 'has-feedback' : '',
40539                             cn: [
40540                                 input
40541                             ]
40542                         }
40543                     ]
40544                 }
40545             ]
40546             
40547         };
40548         
40549         if (this.fieldLabel.length) {
40550             var indicator = {
40551                 tag: 'i',
40552                 tooltip: 'This field is required'
40553             };
40554
40555             var label = {
40556                 tag: 'label',
40557                 'for':  id,
40558                 cls: 'control-label',
40559                 cn: []
40560             };
40561
40562             var label_text = {
40563                 tag: 'span',
40564                 html: this.fieldLabel
40565             };
40566
40567             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40568             label.cn = [
40569                 indicator,
40570                 label_text
40571             ];
40572
40573             if(this.indicatorpos == 'right') {
40574                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40575                 label.cn = [
40576                     label_text,
40577                     indicator
40578                 ];
40579             }
40580
40581             if(align == 'left') {
40582                 container = {
40583                     tag: 'div',
40584                     cn: [
40585                         container
40586                     ]
40587                 };
40588
40589                 if(this.labelWidth > 12){
40590                     label.style = "width: " + this.labelWidth + 'px';
40591                 }
40592                 if(this.labelWidth < 13 && this.labelmd == 0){
40593                     this.labelmd = this.labelWidth;
40594                 }
40595                 if(this.labellg > 0){
40596                     label.cls += ' col-lg-' + this.labellg;
40597                     input.cls += ' col-lg-' + (12 - this.labellg);
40598                 }
40599                 if(this.labelmd > 0){
40600                     label.cls += ' col-md-' + this.labelmd;
40601                     container.cls += ' col-md-' + (12 - this.labelmd);
40602                 }
40603                 if(this.labelsm > 0){
40604                     label.cls += ' col-sm-' + this.labelsm;
40605                     container.cls += ' col-sm-' + (12 - this.labelsm);
40606                 }
40607                 if(this.labelxs > 0){
40608                     label.cls += ' col-xs-' + this.labelxs;
40609                     container.cls += ' col-xs-' + (12 - this.labelxs);
40610                 }
40611             }
40612         }
40613
40614         cfg.cn = [
40615             label,
40616             container,
40617             hiddenInput
40618         ];
40619         
40620         var settings = this;
40621
40622         ['xs','sm','md','lg'].map(function(size){
40623             if (settings[size]) {
40624                 cfg.cls += ' col-' + size + '-' + settings[size];
40625             }
40626         });
40627         
40628         return cfg;
40629     },
40630     
40631     initEvents : function()
40632     {
40633         this.indicator = this.indicatorEl();
40634         
40635         this.initCurrencyEvent();
40636         
40637         this.initNumberEvent();
40638     },
40639     
40640     initCurrencyEvent : function()
40641     {
40642         if (!this.store) {
40643             throw "can not find store for combo";
40644         }
40645         
40646         this.store = Roo.factory(this.store, Roo.data);
40647         this.store.parent = this;
40648         
40649         this.createList();
40650         
40651         this.triggerEl = this.el.select('.input-group-addon', true).first();
40652         
40653         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40654         
40655         var _this = this;
40656         
40657         (function(){
40658             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40659             _this.list.setWidth(lw);
40660         }).defer(100);
40661         
40662         this.list.on('mouseover', this.onViewOver, this);
40663         this.list.on('mousemove', this.onViewMove, this);
40664         this.list.on('scroll', this.onViewScroll, this);
40665         
40666         if(!this.tpl){
40667             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40668         }
40669         
40670         this.view = new Roo.View(this.list, this.tpl, {
40671             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40672         });
40673         
40674         this.view.on('click', this.onViewClick, this);
40675         
40676         this.store.on('beforeload', this.onBeforeLoad, this);
40677         this.store.on('load', this.onLoad, this);
40678         this.store.on('loadexception', this.onLoadException, this);
40679         
40680         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40681             "up" : function(e){
40682                 this.inKeyMode = true;
40683                 this.selectPrev();
40684             },
40685
40686             "down" : function(e){
40687                 if(!this.isExpanded()){
40688                     this.onTriggerClick();
40689                 }else{
40690                     this.inKeyMode = true;
40691                     this.selectNext();
40692                 }
40693             },
40694
40695             "enter" : function(e){
40696                 this.collapse();
40697                 
40698                 if(this.fireEvent("specialkey", this, e)){
40699                     this.onViewClick(false);
40700                 }
40701                 
40702                 return true;
40703             },
40704
40705             "esc" : function(e){
40706                 this.collapse();
40707             },
40708
40709             "tab" : function(e){
40710                 this.collapse();
40711                 
40712                 if(this.fireEvent("specialkey", this, e)){
40713                     this.onViewClick(false);
40714                 }
40715                 
40716                 return true;
40717             },
40718
40719             scope : this,
40720
40721             doRelay : function(foo, bar, hname){
40722                 if(hname == 'down' || this.scope.isExpanded()){
40723                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40724                 }
40725                 return true;
40726             },
40727
40728             forceKeyDown: true
40729         });
40730         
40731         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40732         
40733     },
40734     
40735     initNumberEvent : function(e)
40736     {
40737         this.inputEl().on("keydown" , this.fireKey,  this);
40738         this.inputEl().on("focus", this.onFocus,  this);
40739         this.inputEl().on("blur", this.onBlur,  this);
40740         
40741         this.inputEl().relayEvent('keyup', this);
40742         
40743         if(this.indicator){
40744             this.indicator.addClass('invisible');
40745         }
40746  
40747         this.originalValue = this.getValue();
40748         
40749         if(this.validationEvent == 'keyup'){
40750             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40751             this.inputEl().on('keyup', this.filterValidation, this);
40752         }
40753         else if(this.validationEvent !== false){
40754             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40755         }
40756         
40757         if(this.selectOnFocus){
40758             this.on("focus", this.preFocus, this);
40759             
40760         }
40761         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40762             this.inputEl().on("keypress", this.filterKeys, this);
40763         } else {
40764             this.inputEl().relayEvent('keypress', this);
40765         }
40766         
40767         var allowed = "0123456789";
40768         
40769         if(this.allowDecimals){
40770             allowed += this.decimalSeparator;
40771         }
40772         
40773         if(this.allowNegative){
40774             allowed += "-";
40775         }
40776         
40777         if(this.thousandsDelimiter) {
40778             allowed += ",";
40779         }
40780         
40781         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40782         
40783         var keyPress = function(e){
40784             
40785             var k = e.getKey();
40786             
40787             var c = e.getCharCode();
40788             
40789             if(
40790                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40791                     allowed.indexOf(String.fromCharCode(c)) === -1
40792             ){
40793                 e.stopEvent();
40794                 return;
40795             }
40796             
40797             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40798                 return;
40799             }
40800             
40801             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40802                 e.stopEvent();
40803             }
40804         };
40805         
40806         this.inputEl().on("keypress", keyPress, this);
40807         
40808     },
40809     
40810     onTriggerClick : function(e)
40811     {   
40812         if(this.disabled){
40813             return;
40814         }
40815         
40816         this.page = 0;
40817         this.loadNext = false;
40818         
40819         if(this.isExpanded()){
40820             this.collapse();
40821             return;
40822         }
40823         
40824         this.hasFocus = true;
40825         
40826         if(this.triggerAction == 'all') {
40827             this.doQuery(this.allQuery, true);
40828             return;
40829         }
40830         
40831         this.doQuery(this.getRawValue());
40832     },
40833     
40834     getCurrency : function()
40835     {   
40836         var v = this.currencyEl().getValue();
40837         
40838         return v;
40839     },
40840     
40841     restrictHeight : function()
40842     {
40843         this.list.alignTo(this.currencyEl(), this.listAlign);
40844         this.list.alignTo(this.currencyEl(), this.listAlign);
40845     },
40846     
40847     onViewClick : function(view, doFocus, el, e)
40848     {
40849         var index = this.view.getSelectedIndexes()[0];
40850         
40851         var r = this.store.getAt(index);
40852         
40853         if(r){
40854             this.onSelect(r, index);
40855         }
40856     },
40857     
40858     onSelect : function(record, index){
40859         
40860         if(this.fireEvent('beforeselect', this, record, index) !== false){
40861         
40862             this.setFromCurrencyData(index > -1 ? record.data : false);
40863             
40864             this.collapse();
40865             
40866             this.fireEvent('select', this, record, index);
40867         }
40868     },
40869     
40870     setFromCurrencyData : function(o)
40871     {
40872         var currency = '';
40873         
40874         this.lastCurrency = o;
40875         
40876         if (this.currencyField) {
40877             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40878         } else {
40879             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40880         }
40881         
40882         this.lastSelectionText = currency;
40883         
40884         //setting default currency
40885         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40886             this.setCurrency(this.defaultCurrency);
40887             return;
40888         }
40889         
40890         this.setCurrency(currency);
40891     },
40892     
40893     setFromData : function(o)
40894     {
40895         var c = {};
40896         
40897         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40898         
40899         this.setFromCurrencyData(c);
40900         
40901         var value = '';
40902         
40903         if (this.name) {
40904             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40905         } else {
40906             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40907         }
40908         
40909         this.setValue(value);
40910         
40911     },
40912     
40913     setCurrency : function(v)
40914     {   
40915         this.currencyValue = v;
40916         
40917         if(this.rendered){
40918             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40919             this.validate();
40920         }
40921     },
40922     
40923     setValue : function(v)
40924     {
40925         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40926         
40927         this.value = v;
40928         
40929         if(this.rendered){
40930             
40931             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40932             
40933             this.inputEl().dom.value = (v == '') ? '' :
40934                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40935             
40936             if(!this.allowZero && v === '0') {
40937                 this.hiddenEl().dom.value = '';
40938                 this.inputEl().dom.value = '';
40939             }
40940             
40941             this.validate();
40942         }
40943     },
40944     
40945     getRawValue : function()
40946     {
40947         var v = this.inputEl().getValue();
40948         
40949         return v;
40950     },
40951     
40952     getValue : function()
40953     {
40954         return this.fixPrecision(this.parseValue(this.getRawValue()));
40955     },
40956     
40957     parseValue : function(value)
40958     {
40959         if(this.thousandsDelimiter) {
40960             value += "";
40961             r = new RegExp(",", "g");
40962             value = value.replace(r, "");
40963         }
40964         
40965         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40966         return isNaN(value) ? '' : value;
40967         
40968     },
40969     
40970     fixPrecision : function(value)
40971     {
40972         if(this.thousandsDelimiter) {
40973             value += "";
40974             r = new RegExp(",", "g");
40975             value = value.replace(r, "");
40976         }
40977         
40978         var nan = isNaN(value);
40979         
40980         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40981             return nan ? '' : value;
40982         }
40983         return parseFloat(value).toFixed(this.decimalPrecision);
40984     },
40985     
40986     decimalPrecisionFcn : function(v)
40987     {
40988         return Math.floor(v);
40989     },
40990     
40991     validateValue : function(value)
40992     {
40993         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40994             return false;
40995         }
40996         
40997         var num = this.parseValue(value);
40998         
40999         if(isNaN(num)){
41000             this.markInvalid(String.format(this.nanText, value));
41001             return false;
41002         }
41003         
41004         if(num < this.minValue){
41005             this.markInvalid(String.format(this.minText, this.minValue));
41006             return false;
41007         }
41008         
41009         if(num > this.maxValue){
41010             this.markInvalid(String.format(this.maxText, this.maxValue));
41011             return false;
41012         }
41013         
41014         return true;
41015     },
41016     
41017     validate : function()
41018     {
41019         if(this.disabled || this.allowBlank){
41020             this.markValid();
41021             return true;
41022         }
41023         
41024         var currency = this.getCurrency();
41025         
41026         if(this.validateValue(this.getRawValue()) && currency.length){
41027             this.markValid();
41028             return true;
41029         }
41030         
41031         this.markInvalid();
41032         return false;
41033     },
41034     
41035     getName: function()
41036     {
41037         return this.name;
41038     },
41039     
41040     beforeBlur : function()
41041     {
41042         if(!this.castInt){
41043             return;
41044         }
41045         
41046         var v = this.parseValue(this.getRawValue());
41047         
41048         if(v || v == 0){
41049             this.setValue(v);
41050         }
41051     },
41052     
41053     onBlur : function()
41054     {
41055         this.beforeBlur();
41056         
41057         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41058             //this.el.removeClass(this.focusClass);
41059         }
41060         
41061         this.hasFocus = false;
41062         
41063         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41064             this.validate();
41065         }
41066         
41067         var v = this.getValue();
41068         
41069         if(String(v) !== String(this.startValue)){
41070             this.fireEvent('change', this, v, this.startValue);
41071         }
41072         
41073         this.fireEvent("blur", this);
41074     },
41075     
41076     inputEl : function()
41077     {
41078         return this.el.select('.roo-money-amount-input', true).first();
41079     },
41080     
41081     currencyEl : function()
41082     {
41083         return this.el.select('.roo-money-currency-input', true).first();
41084     },
41085     
41086     hiddenEl : function()
41087     {
41088         return this.el.select('input.hidden-number-input',true).first();
41089     }
41090     
41091 });