sync
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     hide: function() {
921        
922      
923         this.el.hide();   
924     },
925     show: function() {
926        
927         this.el.show();   
928     },
929     setWeight : function(str)
930     {
931         this.el.removeClass(this.weightClass);
932         this.el.addClass('btn-' + str);        
933     }
934     
935     
936 });
937
938  /*
939  * - LGPL
940  *
941  * column
942  * 
943  */
944
945 /**
946  * @class Roo.bootstrap.Column
947  * @extends Roo.bootstrap.Component
948  * Bootstrap Column class
949  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
950  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
951  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
952  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
953  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
954  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
955  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
956  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
957  *
958  * 
959  * @cfg {Boolean} hidden (true|false) hide the element
960  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
961  * @cfg {String} fa (ban|check|...) font awesome icon
962  * @cfg {Number} fasize (1|2|....) font awsome size
963
964  * @cfg {String} icon (info-sign|check|...) glyphicon name
965
966  * @cfg {String} html content of column.
967  * 
968  * @constructor
969  * Create a new Column
970  * @param {Object} config The config object
971  */
972
973 Roo.bootstrap.Column = function(config){
974     Roo.bootstrap.Column.superclass.constructor.call(this, config);
975 };
976
977 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
978     
979     xs: false,
980     sm: false,
981     md: false,
982     lg: false,
983     xsoff: false,
984     smoff: false,
985     mdoff: false,
986     lgoff: false,
987     html: '',
988     offset: 0,
989     alert: false,
990     fa: false,
991     icon : false,
992     hidden : false,
993     fasize : 1,
994     
995     getAutoCreate : function(){
996         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
997         
998         cfg = {
999             tag: 'div',
1000             cls: 'column'
1001         };
1002         
1003         var settings=this;
1004         ['xs','sm','md','lg'].map(function(size){
1005             //Roo.log( size + ':' + settings[size]);
1006             
1007             if (settings[size+'off'] !== false) {
1008                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1009             }
1010             
1011             if (settings[size] === false) {
1012                 return;
1013             }
1014             
1015             if (!settings[size]) { // 0 = hidden
1016                 cfg.cls += ' hidden-' + size;
1017                 return;
1018             }
1019             cfg.cls += ' col-' + size + '-' + settings[size];
1020             
1021         });
1022         
1023         if (this.hidden) {
1024             cfg.cls += ' hidden';
1025         }
1026         
1027         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028             cfg.cls +=' alert alert-' + this.alert;
1029         }
1030         
1031         
1032         if (this.html.length) {
1033             cfg.html = this.html;
1034         }
1035         if (this.fa) {
1036             var fasize = '';
1037             if (this.fasize > 1) {
1038                 fasize = ' fa-' + this.fasize + 'x';
1039             }
1040             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1041             
1042             
1043         }
1044         if (this.icon) {
1045             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1046         }
1047         
1048         return cfg;
1049     }
1050    
1051 });
1052
1053  
1054
1055  /*
1056  * - LGPL
1057  *
1058  * page container.
1059  * 
1060  */
1061
1062
1063 /**
1064  * @class Roo.bootstrap.Container
1065  * @extends Roo.bootstrap.Component
1066  * Bootstrap Container class
1067  * @cfg {Boolean} jumbotron is it a jumbotron element
1068  * @cfg {String} html content of element
1069  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1070  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1071  * @cfg {String} header content of header (for panel)
1072  * @cfg {String} footer content of footer (for panel)
1073  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1074  * @cfg {String} tag (header|aside|section) type of HTML tag.
1075  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1076  * @cfg {String} fa font awesome icon
1077  * @cfg {String} icon (info-sign|check|...) glyphicon name
1078  * @cfg {Boolean} hidden (true|false) hide the element
1079  * @cfg {Boolean} expandable (true|false) default false
1080  * @cfg {Boolean} expanded (true|false) default true
1081  * @cfg {String} rheader contet on the right of header
1082  * @cfg {Boolean} clickable (true|false) default false
1083
1084  *     
1085  * @constructor
1086  * Create a new Container
1087  * @param {Object} config The config object
1088  */
1089
1090 Roo.bootstrap.Container = function(config){
1091     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1092     
1093     this.addEvents({
1094         // raw events
1095          /**
1096          * @event expand
1097          * After the panel has been expand
1098          * 
1099          * @param {Roo.bootstrap.Container} this
1100          */
1101         "expand" : true,
1102         /**
1103          * @event collapse
1104          * After the panel has been collapsed
1105          * 
1106          * @param {Roo.bootstrap.Container} this
1107          */
1108         "collapse" : true,
1109         /**
1110          * @event click
1111          * When a element is chick
1112          * @param {Roo.bootstrap.Container} this
1113          * @param {Roo.EventObject} e
1114          */
1115         "click" : true
1116     });
1117 };
1118
1119 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1120     
1121     jumbotron : false,
1122     well: '',
1123     panel : '',
1124     header: '',
1125     footer : '',
1126     sticky: '',
1127     tag : false,
1128     alert : false,
1129     fa: false,
1130     icon : false,
1131     expandable : false,
1132     rheader : '',
1133     expanded : true,
1134     clickable: false,
1135   
1136      
1137     getChildContainer : function() {
1138         
1139         if(!this.el){
1140             return false;
1141         }
1142         
1143         if (this.panel.length) {
1144             return this.el.select('.panel-body',true).first();
1145         }
1146         
1147         return this.el;
1148     },
1149     
1150     
1151     getAutoCreate : function(){
1152         
1153         var cfg = {
1154             tag : this.tag || 'div',
1155             html : '',
1156             cls : ''
1157         };
1158         if (this.jumbotron) {
1159             cfg.cls = 'jumbotron';
1160         }
1161         
1162         
1163         
1164         // - this is applied by the parent..
1165         //if (this.cls) {
1166         //    cfg.cls = this.cls + '';
1167         //}
1168         
1169         if (this.sticky.length) {
1170             
1171             var bd = Roo.get(document.body);
1172             if (!bd.hasClass('bootstrap-sticky')) {
1173                 bd.addClass('bootstrap-sticky');
1174                 Roo.select('html',true).setStyle('height', '100%');
1175             }
1176              
1177             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1178         }
1179         
1180         
1181         if (this.well.length) {
1182             switch (this.well) {
1183                 case 'lg':
1184                 case 'sm':
1185                     cfg.cls +=' well well-' +this.well;
1186                     break;
1187                 default:
1188                     cfg.cls +=' well';
1189                     break;
1190             }
1191         }
1192         
1193         if (this.hidden) {
1194             cfg.cls += ' hidden';
1195         }
1196         
1197         
1198         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1199             cfg.cls +=' alert alert-' + this.alert;
1200         }
1201         
1202         var body = cfg;
1203         
1204         if (this.panel.length) {
1205             cfg.cls += ' panel panel-' + this.panel;
1206             cfg.cn = [];
1207             if (this.header.length) {
1208                 
1209                 var h = [];
1210                 
1211                 if(this.expandable){
1212                     
1213                     cfg.cls = cfg.cls + ' expandable';
1214                     
1215                     h.push({
1216                         tag: 'i',
1217                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1218                     });
1219                     
1220                 }
1221                 
1222                 h.push(
1223                     {
1224                         tag: 'span',
1225                         cls : 'panel-title',
1226                         html : (this.expandable ? '&nbsp;' : '') + this.header
1227                     },
1228                     {
1229                         tag: 'span',
1230                         cls: 'panel-header-right',
1231                         html: this.rheader
1232                     }
1233                 );
1234                 
1235                 cfg.cn.push({
1236                     cls : 'panel-heading',
1237                     style : this.expandable ? 'cursor: pointer' : '',
1238                     cn : h
1239                 });
1240                 
1241             }
1242             
1243             body = false;
1244             cfg.cn.push({
1245                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1246                 html : this.html
1247             });
1248             
1249             
1250             if (this.footer.length) {
1251                 cfg.cn.push({
1252                     cls : 'panel-footer',
1253                     html : this.footer
1254                     
1255                 });
1256             }
1257             
1258         }
1259         
1260         if (body) {
1261             body.html = this.html || cfg.html;
1262             // prefix with the icons..
1263             if (this.fa) {
1264                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1265             }
1266             if (this.icon) {
1267                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1268             }
1269             
1270             
1271         }
1272         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1273             cfg.cls =  'container';
1274         }
1275         
1276         return cfg;
1277     },
1278     
1279     initEvents: function() 
1280     {
1281         if(this.expandable){
1282             var headerEl = this.headerEl();
1283         
1284             if(headerEl){
1285                 headerEl.on('click', this.onToggleClick, this);
1286             }
1287         }
1288         
1289         if(this.clickable){
1290             this.el.on('click', this.onClick, this);
1291         }
1292         
1293     },
1294     
1295     onToggleClick : function()
1296     {
1297         var headerEl = this.headerEl();
1298         
1299         if(!headerEl){
1300             return;
1301         }
1302         
1303         if(this.expanded){
1304             this.collapse();
1305             return;
1306         }
1307         
1308         this.expand();
1309     },
1310     
1311     expand : function()
1312     {
1313         if(this.fireEvent('expand', this)) {
1314             
1315             this.expanded = true;
1316             
1317             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1318             
1319             this.el.select('.panel-body',true).first().removeClass('hide');
1320             
1321             var toggleEl = this.toggleEl();
1322
1323             if(!toggleEl){
1324                 return;
1325             }
1326
1327             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1328         }
1329         
1330     },
1331     
1332     collapse : function()
1333     {
1334         if(this.fireEvent('collapse', this)) {
1335             
1336             this.expanded = false;
1337             
1338             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1339             this.el.select('.panel-body',true).first().addClass('hide');
1340         
1341             var toggleEl = this.toggleEl();
1342
1343             if(!toggleEl){
1344                 return;
1345             }
1346
1347             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1348         }
1349     },
1350     
1351     toggleEl : function()
1352     {
1353         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1354             return;
1355         }
1356         
1357         return this.el.select('.panel-heading .fa',true).first();
1358     },
1359     
1360     headerEl : function()
1361     {
1362         if(!this.el || !this.panel.length || !this.header.length){
1363             return;
1364         }
1365         
1366         return this.el.select('.panel-heading',true).first()
1367     },
1368     
1369     bodyEl : function()
1370     {
1371         if(!this.el || !this.panel.length){
1372             return;
1373         }
1374         
1375         return this.el.select('.panel-body',true).first()
1376     },
1377     
1378     titleEl : function()
1379     {
1380         if(!this.el || !this.panel.length || !this.header.length){
1381             return;
1382         }
1383         
1384         return this.el.select('.panel-title',true).first();
1385     },
1386     
1387     setTitle : function(v)
1388     {
1389         var titleEl = this.titleEl();
1390         
1391         if(!titleEl){
1392             return;
1393         }
1394         
1395         titleEl.dom.innerHTML = v;
1396     },
1397     
1398     getTitle : function()
1399     {
1400         
1401         var titleEl = this.titleEl();
1402         
1403         if(!titleEl){
1404             return '';
1405         }
1406         
1407         return titleEl.dom.innerHTML;
1408     },
1409     
1410     setRightTitle : function(v)
1411     {
1412         var t = this.el.select('.panel-header-right',true).first();
1413         
1414         if(!t){
1415             return;
1416         }
1417         
1418         t.dom.innerHTML = v;
1419     },
1420     
1421     onClick : function(e)
1422     {
1423         e.preventDefault();
1424         
1425         this.fireEvent('click', this, e);
1426     }
1427 });
1428
1429  /*
1430  * - LGPL
1431  *
1432  * image
1433  * 
1434  */
1435
1436
1437 /**
1438  * @class Roo.bootstrap.Img
1439  * @extends Roo.bootstrap.Component
1440  * Bootstrap Img class
1441  * @cfg {Boolean} imgResponsive false | true
1442  * @cfg {String} border rounded | circle | thumbnail
1443  * @cfg {String} src image source
1444  * @cfg {String} alt image alternative text
1445  * @cfg {String} href a tag href
1446  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1447  * @cfg {String} xsUrl xs image source
1448  * @cfg {String} smUrl sm image source
1449  * @cfg {String} mdUrl md image source
1450  * @cfg {String} lgUrl lg image source
1451  * 
1452  * @constructor
1453  * Create a new Input
1454  * @param {Object} config The config object
1455  */
1456
1457 Roo.bootstrap.Img = function(config){
1458     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1459     
1460     this.addEvents({
1461         // img events
1462         /**
1463          * @event click
1464          * The img click event for the img.
1465          * @param {Roo.EventObject} e
1466          */
1467         "click" : true
1468     });
1469 };
1470
1471 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1472     
1473     imgResponsive: true,
1474     border: '',
1475     src: 'about:blank',
1476     href: false,
1477     target: false,
1478     xsUrl: '',
1479     smUrl: '',
1480     mdUrl: '',
1481     lgUrl: '',
1482
1483     getAutoCreate : function()
1484     {   
1485         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1486             return this.createSingleImg();
1487         }
1488         
1489         var cfg = {
1490             tag: 'div',
1491             cls: 'roo-image-responsive-group',
1492             cn: []
1493         };
1494         var _this = this;
1495         
1496         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1497             
1498             if(!_this[size + 'Url']){
1499                 return;
1500             }
1501             
1502             var img = {
1503                 tag: 'img',
1504                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1505                 html: _this.html || cfg.html,
1506                 src: _this[size + 'Url']
1507             };
1508             
1509             img.cls += ' roo-image-responsive-' + size;
1510             
1511             var s = ['xs', 'sm', 'md', 'lg'];
1512             
1513             s.splice(s.indexOf(size), 1);
1514             
1515             Roo.each(s, function(ss){
1516                 img.cls += ' hidden-' + ss;
1517             });
1518             
1519             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1520                 cfg.cls += ' img-' + _this.border;
1521             }
1522             
1523             if(_this.alt){
1524                 cfg.alt = _this.alt;
1525             }
1526             
1527             if(_this.href){
1528                 var a = {
1529                     tag: 'a',
1530                     href: _this.href,
1531                     cn: [
1532                         img
1533                     ]
1534                 };
1535
1536                 if(this.target){
1537                     a.target = _this.target;
1538                 }
1539             }
1540             
1541             cfg.cn.push((_this.href) ? a : img);
1542             
1543         });
1544         
1545         return cfg;
1546     },
1547     
1548     createSingleImg : function()
1549     {
1550         var cfg = {
1551             tag: 'img',
1552             cls: (this.imgResponsive) ? 'img-responsive' : '',
1553             html : null,
1554             src : 'about:blank'  // just incase src get's set to undefined?!?
1555         };
1556         
1557         cfg.html = this.html || cfg.html;
1558         
1559         cfg.src = this.src || cfg.src;
1560         
1561         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1562             cfg.cls += ' img-' + this.border;
1563         }
1564         
1565         if(this.alt){
1566             cfg.alt = this.alt;
1567         }
1568         
1569         if(this.href){
1570             var a = {
1571                 tag: 'a',
1572                 href: this.href,
1573                 cn: [
1574                     cfg
1575                 ]
1576             };
1577             
1578             if(this.target){
1579                 a.target = this.target;
1580             }
1581             
1582         }
1583         
1584         return (this.href) ? a : cfg;
1585     },
1586     
1587     initEvents: function() 
1588     {
1589         if(!this.href){
1590             this.el.on('click', this.onClick, this);
1591         }
1592         
1593     },
1594     
1595     onClick : function(e)
1596     {
1597         Roo.log('img onclick');
1598         this.fireEvent('click', this, e);
1599     },
1600     /**
1601      * Sets the url of the image - used to update it
1602      * @param {String} url the url of the image
1603      */
1604     
1605     setSrc : function(url)
1606     {
1607         this.src =  url;
1608         
1609         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1610             this.el.dom.src =  url;
1611             return;
1612         }
1613         
1614         this.el.select('img', true).first().dom.src =  url;
1615     }
1616     
1617     
1618    
1619 });
1620
1621  /*
1622  * - LGPL
1623  *
1624  * image
1625  * 
1626  */
1627
1628
1629 /**
1630  * @class Roo.bootstrap.Link
1631  * @extends Roo.bootstrap.Component
1632  * Bootstrap Link Class
1633  * @cfg {String} alt image alternative text
1634  * @cfg {String} href a tag href
1635  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1636  * @cfg {String} html the content of the link.
1637  * @cfg {String} anchor name for the anchor link
1638  * @cfg {String} fa - favicon
1639
1640  * @cfg {Boolean} preventDefault (true | false) default false
1641
1642  * 
1643  * @constructor
1644  * Create a new Input
1645  * @param {Object} config The config object
1646  */
1647
1648 Roo.bootstrap.Link = function(config){
1649     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1650     
1651     this.addEvents({
1652         // img events
1653         /**
1654          * @event click
1655          * The img click event for the img.
1656          * @param {Roo.EventObject} e
1657          */
1658         "click" : true
1659     });
1660 };
1661
1662 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1663     
1664     href: false,
1665     target: false,
1666     preventDefault: false,
1667     anchor : false,
1668     alt : false,
1669     fa: false,
1670
1671
1672     getAutoCreate : function()
1673     {
1674         var html = this.html || '';
1675         
1676         if (this.fa !== false) {
1677             html = '<i class="fa fa-' + this.fa + '"></i>';
1678         }
1679         var cfg = {
1680             tag: 'a'
1681         };
1682         // anchor's do not require html/href...
1683         if (this.anchor === false) {
1684             cfg.html = html;
1685             cfg.href = this.href || '#';
1686         } else {
1687             cfg.name = this.anchor;
1688             if (this.html !== false || this.fa !== false) {
1689                 cfg.html = html;
1690             }
1691             if (this.href !== false) {
1692                 cfg.href = this.href;
1693             }
1694         }
1695         
1696         if(this.alt !== false){
1697             cfg.alt = this.alt;
1698         }
1699         
1700         
1701         if(this.target !== false) {
1702             cfg.target = this.target;
1703         }
1704         
1705         return cfg;
1706     },
1707     
1708     initEvents: function() {
1709         
1710         if(!this.href || this.preventDefault){
1711             this.el.on('click', this.onClick, this);
1712         }
1713     },
1714     
1715     onClick : function(e)
1716     {
1717         if(this.preventDefault){
1718             e.preventDefault();
1719         }
1720         //Roo.log('img onclick');
1721         this.fireEvent('click', this, e);
1722     }
1723    
1724 });
1725
1726  /*
1727  * - LGPL
1728  *
1729  * header
1730  * 
1731  */
1732
1733 /**
1734  * @class Roo.bootstrap.Header
1735  * @extends Roo.bootstrap.Component
1736  * Bootstrap Header class
1737  * @cfg {String} html content of header
1738  * @cfg {Number} level (1|2|3|4|5|6) default 1
1739  * 
1740  * @constructor
1741  * Create a new Header
1742  * @param {Object} config The config object
1743  */
1744
1745
1746 Roo.bootstrap.Header  = function(config){
1747     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1748 };
1749
1750 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1751     
1752     //href : false,
1753     html : false,
1754     level : 1,
1755     
1756     
1757     
1758     getAutoCreate : function(){
1759         
1760         
1761         
1762         var cfg = {
1763             tag: 'h' + (1 *this.level),
1764             html: this.html || ''
1765         } ;
1766         
1767         return cfg;
1768     }
1769    
1770 });
1771
1772  
1773
1774  /*
1775  * Based on:
1776  * Ext JS Library 1.1.1
1777  * Copyright(c) 2006-2007, Ext JS, LLC.
1778  *
1779  * Originally Released Under LGPL - original licence link has changed is not relivant.
1780  *
1781  * Fork - LGPL
1782  * <script type="text/javascript">
1783  */
1784  
1785 /**
1786  * @class Roo.bootstrap.MenuMgr
1787  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1788  * @singleton
1789  */
1790 Roo.bootstrap.MenuMgr = function(){
1791    var menus, active, groups = {}, attached = false, lastShow = new Date();
1792
1793    // private - called when first menu is created
1794    function init(){
1795        menus = {};
1796        active = new Roo.util.MixedCollection();
1797        Roo.get(document).addKeyListener(27, function(){
1798            if(active.length > 0){
1799                hideAll();
1800            }
1801        });
1802    }
1803
1804    // private
1805    function hideAll(){
1806        if(active && active.length > 0){
1807            var c = active.clone();
1808            c.each(function(m){
1809                m.hide();
1810            });
1811        }
1812    }
1813
1814    // private
1815    function onHide(m){
1816        active.remove(m);
1817        if(active.length < 1){
1818            Roo.get(document).un("mouseup", onMouseDown);
1819             
1820            attached = false;
1821        }
1822    }
1823
1824    // private
1825    function onShow(m){
1826        var last = active.last();
1827        lastShow = new Date();
1828        active.add(m);
1829        if(!attached){
1830           Roo.get(document).on("mouseup", onMouseDown);
1831            
1832            attached = true;
1833        }
1834        if(m.parentMenu){
1835           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1836           m.parentMenu.activeChild = m;
1837        }else if(last && last.isVisible()){
1838           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1839        }
1840    }
1841
1842    // private
1843    function onBeforeHide(m){
1844        if(m.activeChild){
1845            m.activeChild.hide();
1846        }
1847        if(m.autoHideTimer){
1848            clearTimeout(m.autoHideTimer);
1849            delete m.autoHideTimer;
1850        }
1851    }
1852
1853    // private
1854    function onBeforeShow(m){
1855        var pm = m.parentMenu;
1856        if(!pm && !m.allowOtherMenus){
1857            hideAll();
1858        }else if(pm && pm.activeChild && active != m){
1859            pm.activeChild.hide();
1860        }
1861    }
1862
1863    // private this should really trigger on mouseup..
1864    function onMouseDown(e){
1865         Roo.log("on Mouse Up");
1866         
1867         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1868             Roo.log("MenuManager hideAll");
1869             hideAll();
1870             e.stopEvent();
1871         }
1872         
1873         
1874    }
1875
1876    // private
1877    function onBeforeCheck(mi, state){
1878        if(state){
1879            var g = groups[mi.group];
1880            for(var i = 0, l = g.length; i < l; i++){
1881                if(g[i] != mi){
1882                    g[i].setChecked(false);
1883                }
1884            }
1885        }
1886    }
1887
1888    return {
1889
1890        /**
1891         * Hides all menus that are currently visible
1892         */
1893        hideAll : function(){
1894             hideAll();  
1895        },
1896
1897        // private
1898        register : function(menu){
1899            if(!menus){
1900                init();
1901            }
1902            menus[menu.id] = menu;
1903            menu.on("beforehide", onBeforeHide);
1904            menu.on("hide", onHide);
1905            menu.on("beforeshow", onBeforeShow);
1906            menu.on("show", onShow);
1907            var g = menu.group;
1908            if(g && menu.events["checkchange"]){
1909                if(!groups[g]){
1910                    groups[g] = [];
1911                }
1912                groups[g].push(menu);
1913                menu.on("checkchange", onCheck);
1914            }
1915        },
1916
1917         /**
1918          * Returns a {@link Roo.menu.Menu} object
1919          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1920          * be used to generate and return a new Menu instance.
1921          */
1922        get : function(menu){
1923            if(typeof menu == "string"){ // menu id
1924                return menus[menu];
1925            }else if(menu.events){  // menu instance
1926                return menu;
1927            }
1928            /*else if(typeof menu.length == 'number'){ // array of menu items?
1929                return new Roo.bootstrap.Menu({items:menu});
1930            }else{ // otherwise, must be a config
1931                return new Roo.bootstrap.Menu(menu);
1932            }
1933            */
1934            return false;
1935        },
1936
1937        // private
1938        unregister : function(menu){
1939            delete menus[menu.id];
1940            menu.un("beforehide", onBeforeHide);
1941            menu.un("hide", onHide);
1942            menu.un("beforeshow", onBeforeShow);
1943            menu.un("show", onShow);
1944            var g = menu.group;
1945            if(g && menu.events["checkchange"]){
1946                groups[g].remove(menu);
1947                menu.un("checkchange", onCheck);
1948            }
1949        },
1950
1951        // private
1952        registerCheckable : function(menuItem){
1953            var g = menuItem.group;
1954            if(g){
1955                if(!groups[g]){
1956                    groups[g] = [];
1957                }
1958                groups[g].push(menuItem);
1959                menuItem.on("beforecheckchange", onBeforeCheck);
1960            }
1961        },
1962
1963        // private
1964        unregisterCheckable : function(menuItem){
1965            var g = menuItem.group;
1966            if(g){
1967                groups[g].remove(menuItem);
1968                menuItem.un("beforecheckchange", onBeforeCheck);
1969            }
1970        }
1971    };
1972 }();/*
1973  * - LGPL
1974  *
1975  * menu
1976  * 
1977  */
1978
1979 /**
1980  * @class Roo.bootstrap.Menu
1981  * @extends Roo.bootstrap.Component
1982  * Bootstrap Menu class - container for MenuItems
1983  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1984  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1985  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1986  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1987  * 
1988  * @constructor
1989  * Create a new Menu
1990  * @param {Object} config The config object
1991  */
1992
1993
1994 Roo.bootstrap.Menu = function(config){
1995     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1996     if (this.registerMenu && this.type != 'treeview')  {
1997         Roo.bootstrap.MenuMgr.register(this);
1998     }
1999     this.addEvents({
2000         /**
2001          * @event beforeshow
2002          * Fires before this menu is displayed
2003          * @param {Roo.menu.Menu} this
2004          */
2005         beforeshow : true,
2006         /**
2007          * @event beforehide
2008          * Fires before this menu is hidden
2009          * @param {Roo.menu.Menu} this
2010          */
2011         beforehide : true,
2012         /**
2013          * @event show
2014          * Fires after this menu is displayed
2015          * @param {Roo.menu.Menu} this
2016          */
2017         show : true,
2018         /**
2019          * @event hide
2020          * Fires after this menu is hidden
2021          * @param {Roo.menu.Menu} this
2022          */
2023         hide : true,
2024         /**
2025          * @event click
2026          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2027          * @param {Roo.menu.Menu} this
2028          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2029          * @param {Roo.EventObject} e
2030          */
2031         click : true,
2032         /**
2033          * @event mouseover
2034          * Fires when the mouse is hovering over 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         mouseover : true,
2040         /**
2041          * @event mouseout
2042          * Fires when the mouse exits this menu
2043          * @param {Roo.menu.Menu} this
2044          * @param {Roo.EventObject} e
2045          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2046          */
2047         mouseout : true,
2048         /**
2049          * @event itemclick
2050          * Fires when a menu item contained in this menu is clicked
2051          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2052          * @param {Roo.EventObject} e
2053          */
2054         itemclick: true
2055     });
2056     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2057 };
2058
2059 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2060     
2061    /// html : false,
2062     //align : '',
2063     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2064     type: false,
2065     /**
2066      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2067      */
2068     registerMenu : true,
2069     
2070     menuItems :false, // stores the menu items..
2071     
2072     hidden:true,
2073         
2074     parentMenu : false,
2075     
2076     stopEvent : true,
2077     
2078     isLink : false,
2079     
2080     getChildContainer : function() {
2081         return this.el;  
2082     },
2083     
2084     getAutoCreate : function(){
2085          
2086         //if (['right'].indexOf(this.align)!==-1) {
2087         //    cfg.cn[1].cls += ' pull-right'
2088         //}
2089         
2090         
2091         var cfg = {
2092             tag : 'ul',
2093             cls : 'dropdown-menu' ,
2094             style : 'z-index:1000'
2095             
2096         };
2097         
2098         if (this.type === 'submenu') {
2099             cfg.cls = 'submenu active';
2100         }
2101         if (this.type === 'treeview') {
2102             cfg.cls = 'treeview-menu';
2103         }
2104         
2105         return cfg;
2106     },
2107     initEvents : function() {
2108         
2109        // Roo.log("ADD event");
2110        // Roo.log(this.triggerEl.dom);
2111         
2112         this.triggerEl.on('click', this.onTriggerClick, this);
2113         
2114         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2115         
2116         this.triggerEl.addClass('dropdown-toggle');
2117         
2118         if (Roo.isTouch) {
2119             this.el.on('touchstart'  , this.onTouch, this);
2120         }
2121         this.el.on('click' , this.onClick, this);
2122
2123         this.el.on("mouseover", this.onMouseOver, this);
2124         this.el.on("mouseout", this.onMouseOut, this);
2125         
2126     },
2127     
2128     findTargetItem : function(e)
2129     {
2130         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2131         if(!t){
2132             return false;
2133         }
2134         //Roo.log(t);         Roo.log(t.id);
2135         if(t && t.id){
2136             //Roo.log(this.menuitems);
2137             return this.menuitems.get(t.id);
2138             
2139             //return this.items.get(t.menuItemId);
2140         }
2141         
2142         return false;
2143     },
2144     
2145     onTouch : function(e) 
2146     {
2147         Roo.log("menu.onTouch");
2148         //e.stopEvent(); this make the user popdown broken
2149         this.onClick(e);
2150     },
2151     
2152     onClick : function(e)
2153     {
2154         Roo.log("menu.onClick");
2155         
2156         var t = this.findTargetItem(e);
2157         if(!t || t.isContainer){
2158             return;
2159         }
2160         Roo.log(e);
2161         /*
2162         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2163             if(t == this.activeItem && t.shouldDeactivate(e)){
2164                 this.activeItem.deactivate();
2165                 delete this.activeItem;
2166                 return;
2167             }
2168             if(t.canActivate){
2169                 this.setActiveItem(t, true);
2170             }
2171             return;
2172             
2173             
2174         }
2175         */
2176        
2177         Roo.log('pass click event');
2178         
2179         t.onClick(e);
2180         
2181         this.fireEvent("click", this, t, e);
2182         
2183         var _this = this;
2184         
2185         if(!t.href.length || t.href == '#'){
2186             (function() { _this.hide(); }).defer(100);
2187         }
2188         
2189     },
2190     
2191     onMouseOver : function(e){
2192         var t  = this.findTargetItem(e);
2193         //Roo.log(t);
2194         //if(t){
2195         //    if(t.canActivate && !t.disabled){
2196         //        this.setActiveItem(t, true);
2197         //    }
2198         //}
2199         
2200         this.fireEvent("mouseover", this, e, t);
2201     },
2202     isVisible : function(){
2203         return !this.hidden;
2204     },
2205      onMouseOut : function(e){
2206         var t  = this.findTargetItem(e);
2207         
2208         //if(t ){
2209         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2210         //        this.activeItem.deactivate();
2211         //        delete this.activeItem;
2212         //    }
2213         //}
2214         this.fireEvent("mouseout", this, e, t);
2215     },
2216     
2217     
2218     /**
2219      * Displays this menu relative to another element
2220      * @param {String/HTMLElement/Roo.Element} element The element to align to
2221      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2222      * the element (defaults to this.defaultAlign)
2223      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2224      */
2225     show : function(el, pos, parentMenu){
2226         this.parentMenu = parentMenu;
2227         if(!this.el){
2228             this.render();
2229         }
2230         this.fireEvent("beforeshow", this);
2231         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2232     },
2233      /**
2234      * Displays this menu at a specific xy position
2235      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2236      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2237      */
2238     showAt : function(xy, parentMenu, /* private: */_e){
2239         this.parentMenu = parentMenu;
2240         if(!this.el){
2241             this.render();
2242         }
2243         if(_e !== false){
2244             this.fireEvent("beforeshow", this);
2245             //xy = this.el.adjustForConstraints(xy);
2246         }
2247         
2248         //this.el.show();
2249         this.hideMenuItems();
2250         this.hidden = false;
2251         this.triggerEl.addClass('open');
2252         
2253         // reassign x when hitting right
2254         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2256         }
2257         
2258         // reassign y when hitting bottom
2259         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2260             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2261         }
2262         
2263         // but the list may align on trigger left or trigger top... should it be a properity?
2264         
2265         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2266             this.el.setXY(xy);
2267         }
2268         
2269         this.focus();
2270         this.fireEvent("show", this);
2271     },
2272     
2273     focus : function(){
2274         return;
2275         if(!this.hidden){
2276             this.doFocus.defer(50, this);
2277         }
2278     },
2279
2280     doFocus : function(){
2281         if(!this.hidden){
2282             this.focusEl.focus();
2283         }
2284     },
2285
2286     /**
2287      * Hides this menu and optionally all parent menus
2288      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2289      */
2290     hide : function(deep)
2291     {
2292         
2293         this.hideMenuItems();
2294         if(this.el && this.isVisible()){
2295             this.fireEvent("beforehide", this);
2296             if(this.activeItem){
2297                 this.activeItem.deactivate();
2298                 this.activeItem = null;
2299             }
2300             this.triggerEl.removeClass('open');;
2301             this.hidden = true;
2302             this.fireEvent("hide", this);
2303         }
2304         if(deep === true && this.parentMenu){
2305             this.parentMenu.hide(true);
2306         }
2307     },
2308     
2309     onTriggerClick : function(e)
2310     {
2311         Roo.log('trigger click');
2312         
2313         var target = e.getTarget();
2314         
2315         Roo.log(target.nodeName.toLowerCase());
2316         
2317         if(target.nodeName.toLowerCase() === 'i'){
2318             e.preventDefault();
2319         }
2320         
2321     },
2322     
2323     onTriggerPress  : function(e)
2324     {
2325         Roo.log('trigger press');
2326         //Roo.log(e.getTarget());
2327        // Roo.log(this.triggerEl.dom);
2328        
2329         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330         var pel = Roo.get(e.getTarget());
2331         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332             Roo.log('is treeview or dropdown?');
2333             return;
2334         }
2335         
2336         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2337             return;
2338         }
2339         
2340         if (this.isVisible()) {
2341             Roo.log('hide');
2342             this.hide();
2343         } else {
2344             Roo.log('show');
2345             this.show(this.triggerEl, false, false);
2346         }
2347         
2348         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2349             e.stopEvent();
2350         }
2351         
2352     },
2353        
2354     
2355     hideMenuItems : function()
2356     {
2357         Roo.log("hide Menu Items");
2358         if (!this.el) { 
2359             return;
2360         }
2361         //$(backdrop).remove()
2362         this.el.select('.open',true).each(function(aa) {
2363             
2364             aa.removeClass('open');
2365           //var parent = getParent($(this))
2366           //var relatedTarget = { relatedTarget: this }
2367           
2368            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369           //if (e.isDefaultPrevented()) return
2370            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2371         });
2372     },
2373     addxtypeChild : function (tree, cntr) {
2374         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2375           
2376         this.menuitems.add(comp);
2377         return comp;
2378
2379     },
2380     getEl : function()
2381     {
2382         Roo.log(this.el);
2383         return this.el;
2384     },
2385     
2386     clear : function()
2387     {
2388         this.getEl().dom.innerHTML = '';
2389         this.menuitems.clear();
2390     }
2391 });
2392
2393  
2394  /*
2395  * - LGPL
2396  *
2397  * menu item
2398  * 
2399  */
2400
2401
2402 /**
2403  * @class Roo.bootstrap.MenuItem
2404  * @extends Roo.bootstrap.Component
2405  * Bootstrap MenuItem class
2406  * @cfg {String} html the menu label
2407  * @cfg {String} href the link
2408  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2411  * @cfg {String} fa favicon to show on left of menu item.
2412  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2413  * 
2414  * 
2415  * @constructor
2416  * Create a new MenuItem
2417  * @param {Object} config The config object
2418  */
2419
2420
2421 Roo.bootstrap.MenuItem = function(config){
2422     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2423     this.addEvents({
2424         // raw events
2425         /**
2426          * @event click
2427          * The raw click event for the entire grid.
2428          * @param {Roo.bootstrap.MenuItem} this
2429          * @param {Roo.EventObject} e
2430          */
2431         "click" : true
2432     });
2433 };
2434
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2436     
2437     href : false,
2438     html : false,
2439     preventDefault: false,
2440     isContainer : false,
2441     active : false,
2442     fa: false,
2443     
2444     getAutoCreate : function(){
2445         
2446         if(this.isContainer){
2447             return {
2448                 tag: 'li',
2449                 cls: 'dropdown-menu-item'
2450             };
2451         }
2452         var ctag = {
2453             tag: 'span',
2454             html: 'Link'
2455         };
2456         
2457         var anc = {
2458             tag : 'a',
2459             href : '#',
2460             cn : [  ]
2461         };
2462         
2463         if (this.fa !== false) {
2464             anc.cn.push({
2465                 tag : 'i',
2466                 cls : 'fa fa-' + this.fa
2467             });
2468         }
2469         
2470         anc.cn.push(ctag);
2471         
2472         
2473         var cfg= {
2474             tag: 'li',
2475             cls: 'dropdown-menu-item',
2476             cn: [ anc ]
2477         };
2478         if (this.parent().type == 'treeview') {
2479             cfg.cls = 'treeview-menu';
2480         }
2481         if (this.active) {
2482             cfg.cls += ' active';
2483         }
2484         
2485         
2486         
2487         anc.href = this.href || cfg.cn[0].href ;
2488         ctag.html = this.html || cfg.cn[0].html ;
2489         return cfg;
2490     },
2491     
2492     initEvents: function()
2493     {
2494         if (this.parent().type == 'treeview') {
2495             this.el.select('a').on('click', this.onClick, this);
2496         }
2497         
2498         if (this.menu) {
2499             this.menu.parentType = this.xtype;
2500             this.menu.triggerEl = this.el;
2501             this.menu = this.addxtype(Roo.apply({}, this.menu));
2502         }
2503         
2504     },
2505     onClick : function(e)
2506     {
2507         Roo.log('item on click ');
2508         
2509         if(this.preventDefault){
2510             e.preventDefault();
2511         }
2512         //this.parent().hideMenuItems();
2513         
2514         this.fireEvent('click', this, e);
2515     },
2516     getEl : function()
2517     {
2518         return this.el;
2519     } 
2520 });
2521
2522  
2523
2524  /*
2525  * - LGPL
2526  *
2527  * menu separator
2528  * 
2529  */
2530
2531
2532 /**
2533  * @class Roo.bootstrap.MenuSeparator
2534  * @extends Roo.bootstrap.Component
2535  * Bootstrap MenuSeparator class
2536  * 
2537  * @constructor
2538  * Create a new MenuItem
2539  * @param {Object} config The config object
2540  */
2541
2542
2543 Roo.bootstrap.MenuSeparator = function(config){
2544     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2545 };
2546
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2548     
2549     getAutoCreate : function(){
2550         var cfg = {
2551             cls: 'divider',
2552             tag : 'li'
2553         };
2554         
2555         return cfg;
2556     }
2557    
2558 });
2559
2560  
2561
2562  
2563 /*
2564 * Licence: LGPL
2565 */
2566
2567 /**
2568  * @class Roo.bootstrap.Modal
2569  * @extends Roo.bootstrap.Component
2570  * Bootstrap Modal class
2571  * @cfg {String} title Title of dialog
2572  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2574  * @cfg {Boolean} specificTitle default false
2575  * @cfg {Array} buttons Array of buttons or standard button set..
2576  * @cfg {String} buttonPosition (left|right|center) default right
2577  * @cfg {Boolean} animate default true
2578  * @cfg {Boolean} allow_close default true
2579  * @cfg {Boolean} fitwindow default false
2580  * @cfg {String} size (sm|lg) default empty
2581  * @cfg {Number} max_width set the max width of modal
2582  *
2583  *
2584  * @constructor
2585  * Create a new Modal Dialog
2586  * @param {Object} config The config object
2587  */
2588
2589 Roo.bootstrap.Modal = function(config){
2590     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2591     this.addEvents({
2592         // raw events
2593         /**
2594          * @event btnclick
2595          * The raw btnclick event for the button
2596          * @param {Roo.EventObject} e
2597          */
2598         "btnclick" : true,
2599         /**
2600          * @event resize
2601          * Fire when dialog resize
2602          * @param {Roo.bootstrap.Modal} this
2603          * @param {Roo.EventObject} e
2604          */
2605         "resize" : true
2606     });
2607     this.buttons = this.buttons || [];
2608
2609     if (this.tmpl) {
2610         this.tmpl = Roo.factory(this.tmpl);
2611     }
2612
2613 };
2614
2615 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2616
2617     title : 'test dialog',
2618
2619     buttons : false,
2620
2621     // set on load...
2622
2623     html: false,
2624
2625     tmp: false,
2626
2627     specificTitle: false,
2628
2629     buttonPosition: 'right',
2630
2631     allow_close : true,
2632
2633     animate : true,
2634
2635     fitwindow: false,
2636
2637
2638      // private
2639     dialogEl: false,
2640     bodyEl:  false,
2641     footerEl:  false,
2642     titleEl:  false,
2643     closeEl:  false,
2644
2645     size: '',
2646     
2647     max_width: 0,
2648     
2649     fit_content: false,
2650
2651     onRender : function(ct, position)
2652     {
2653         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2654
2655         if(!this.el){
2656             var cfg = Roo.apply({},  this.getAutoCreate());
2657             cfg.id = Roo.id();
2658             //if(!cfg.name){
2659             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2660             //}
2661             //if (!cfg.name.length) {
2662             //    delete cfg.name;
2663            // }
2664             if (this.cls) {
2665                 cfg.cls += ' ' + this.cls;
2666             }
2667             if (this.style) {
2668                 cfg.style = this.style;
2669             }
2670             this.el = Roo.get(document.body).createChild(cfg, position);
2671         }
2672         //var type = this.el.dom.type;
2673
2674
2675         if(this.tabIndex !== undefined){
2676             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2677         }
2678
2679         this.dialogEl = this.el.select('.modal-dialog',true).first();
2680         this.bodyEl = this.el.select('.modal-body',true).first();
2681         this.closeEl = this.el.select('.modal-header .close', true).first();
2682         this.headerEl = this.el.select('.modal-header',true).first();
2683         this.titleEl = this.el.select('.modal-title',true).first();
2684         this.footerEl = this.el.select('.modal-footer',true).first();
2685
2686         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2687         
2688         //this.el.addClass("x-dlg-modal");
2689
2690         if (this.buttons.length) {
2691             Roo.each(this.buttons, function(bb) {
2692                 var b = Roo.apply({}, bb);
2693                 b.xns = b.xns || Roo.bootstrap;
2694                 b.xtype = b.xtype || 'Button';
2695                 if (typeof(b.listeners) == 'undefined') {
2696                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2697                 }
2698
2699                 var btn = Roo.factory(b);
2700
2701                 btn.render(this.el.select('.modal-footer div').first());
2702
2703             },this);
2704         }
2705         // render the children.
2706         var nitems = [];
2707
2708         if(typeof(this.items) != 'undefined'){
2709             var items = this.items;
2710             delete this.items;
2711
2712             for(var i =0;i < items.length;i++) {
2713                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2714             }
2715         }
2716
2717         this.items = nitems;
2718
2719         // where are these used - they used to be body/close/footer
2720
2721
2722         this.initEvents();
2723         //this.el.addClass([this.fieldClass, this.cls]);
2724
2725     },
2726
2727     getAutoCreate : function()
2728     {
2729         var bdy = {
2730                 cls : 'modal-body',
2731                 html : this.html || ''
2732         };
2733
2734         var title = {
2735             tag: 'h4',
2736             cls : 'modal-title',
2737             html : this.title
2738         };
2739
2740         if(this.specificTitle){
2741             title = this.title;
2742
2743         };
2744
2745         var header = [];
2746         if (this.allow_close) {
2747             header.push({
2748                 tag: 'button',
2749                 cls : 'close',
2750                 html : '&times'
2751             });
2752         }
2753
2754         header.push(title);
2755
2756         var size = '';
2757
2758         if(this.size.length){
2759             size = 'modal-' + this.size;
2760         }
2761
2762         var modal = {
2763             cls: "modal",
2764              cn : [
2765                 {
2766                     cls: "modal-dialog " + size,
2767                     cn : [
2768                         {
2769                             cls : "modal-content",
2770                             cn : [
2771                                 {
2772                                     cls : 'modal-header',
2773                                     cn : header
2774                                 },
2775                                 bdy,
2776                                 {
2777                                     cls : 'modal-footer',
2778                                     cn : [
2779                                         {
2780                                             tag: 'div',
2781                                             cls: 'btn-' + this.buttonPosition
2782                                         }
2783                                     ]
2784
2785                                 }
2786
2787
2788                             ]
2789
2790                         }
2791                     ]
2792
2793                 }
2794             ]
2795         };
2796
2797         if(this.animate){
2798             modal.cls += ' fade';
2799         }
2800
2801         return modal;
2802
2803     },
2804     getChildContainer : function() {
2805
2806          return this.bodyEl;
2807
2808     },
2809     getButtonContainer : function() {
2810          return this.el.select('.modal-footer div',true).first();
2811
2812     },
2813     initEvents : function()
2814     {
2815         if (this.allow_close) {
2816             this.closeEl.on('click', this.hide, this);
2817         }
2818         Roo.EventManager.onWindowResize(this.resize, this, true);
2819
2820
2821     },
2822
2823     resize : function()
2824     {
2825         this.maskEl.setSize(
2826             Roo.lib.Dom.getViewWidth(true),
2827             Roo.lib.Dom.getViewHeight(true)
2828         );
2829         
2830         if (this.fitwindow) {
2831             this.setSize(
2832                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2833                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2834             );
2835             return;
2836         }
2837         
2838         if(this.max_width !== 0) {
2839             
2840             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2841             
2842             if(this.height) {
2843                 this.setSize(
2844                     w,
2845                     this.height <= Roo.lib.Dom.getViewportHeight(true) - 60 ? 
2846                         this.height : Roo.lib.Dom.getViewportHeight(true) - 60
2847                 );
2848                 return;
2849             }
2850             
2851             if(!this.fit_content) {
2852                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2853                 return;
2854             }
2855             
2856             this.setSize(w, Math.min(
2857                 60 +
2858                 this.headerEl.getHeight() + 
2859                 this.footerEl.getHeight() + 
2860                 this.getChildHeight(this.bodyEl.dom.childNodes),
2861                 Roo.lib.Dom.getViewportHeight(true) - 60)
2862             );
2863         }
2864         
2865     },
2866
2867     setSize : function(w,h)
2868     {
2869         if (!w && !h) {
2870             return;
2871         }
2872         
2873         this.resizeTo(w,h);
2874     },
2875
2876     show : function() {
2877
2878         if (!this.rendered) {
2879             this.render();
2880         }
2881
2882         //this.el.setStyle('display', 'block');
2883         this.el.removeClass('hideing');        
2884         this.el.addClass('show');
2885  
2886         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2887             var _this = this;
2888             (function(){
2889                 this.el.addClass('in');
2890             }).defer(50, this);
2891         }else{
2892             this.el.addClass('in');
2893         }
2894
2895         // not sure how we can show data in here..
2896         //if (this.tmpl) {
2897         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2898         //}
2899
2900         Roo.get(document.body).addClass("x-body-masked");
2901         
2902         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2903         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2904         this.maskEl.addClass('show');
2905         
2906         this.resize();
2907         
2908         this.fireEvent('show', this);
2909
2910         // set zindex here - otherwise it appears to be ignored...
2911         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2912
2913         (function () {
2914             this.items.forEach( function(e) {
2915                 e.layout ? e.layout() : false;
2916
2917             });
2918         }).defer(100,this);
2919
2920     },
2921     hide : function()
2922     {
2923         if(this.fireEvent("beforehide", this) !== false){
2924             this.maskEl.removeClass('show');
2925             Roo.get(document.body).removeClass("x-body-masked");
2926             this.el.removeClass('in');
2927             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2928
2929             if(this.animate){ // why
2930                 this.el.addClass('hideing');
2931                 (function(){
2932                     if (!this.el.hasClass('hideing')) {
2933                         return; // it's been shown again...
2934                     }
2935                     this.el.removeClass('show');
2936                     this.el.removeClass('hideing');
2937                 }).defer(150,this);
2938                 
2939             }else{
2940                  this.el.removeClass('show');
2941             }
2942             this.fireEvent('hide', this);
2943         }
2944     },
2945     isVisible : function()
2946     {
2947         
2948         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2949         
2950     },
2951
2952     addButton : function(str, cb)
2953     {
2954
2955
2956         var b = Roo.apply({}, { html : str } );
2957         b.xns = b.xns || Roo.bootstrap;
2958         b.xtype = b.xtype || 'Button';
2959         if (typeof(b.listeners) == 'undefined') {
2960             b.listeners = { click : cb.createDelegate(this)  };
2961         }
2962
2963         var btn = Roo.factory(b);
2964
2965         btn.render(this.el.select('.modal-footer div').first());
2966
2967         return btn;
2968
2969     },
2970
2971     setDefaultButton : function(btn)
2972     {
2973         //this.el.select('.modal-footer').()
2974     },
2975     diff : false,
2976
2977     resizeTo: function(w,h)
2978     {
2979         // skip.. ?? why??
2980
2981         this.dialogEl.setWidth(w);
2982         if (this.diff === false) {
2983             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2984         }
2985
2986         this.bodyEl.setHeight(h - this.diff);
2987
2988         this.fireEvent('resize', this);
2989
2990     },
2991     setContentSize  : function(w, h)
2992     {
2993
2994     },
2995     onButtonClick: function(btn,e)
2996     {
2997         //Roo.log([a,b,c]);
2998         this.fireEvent('btnclick', btn.name, e);
2999     },
3000      /**
3001      * Set the title of the Dialog
3002      * @param {String} str new Title
3003      */
3004     setTitle: function(str) {
3005         this.titleEl.dom.innerHTML = str;
3006     },
3007     /**
3008      * Set the body of the Dialog
3009      * @param {String} str new Title
3010      */
3011     setBody: function(str) {
3012         this.bodyEl.dom.innerHTML = str;
3013     },
3014     /**
3015      * Set the body of the Dialog using the template
3016      * @param {Obj} data - apply this data to the template and replace the body contents.
3017      */
3018     applyBody: function(obj)
3019     {
3020         if (!this.tmpl) {
3021             Roo.log("Error - using apply Body without a template");
3022             //code
3023         }
3024         this.tmpl.overwrite(this.bodyEl, obj);
3025     },
3026     
3027     getChildHeight : function(child_nodes)
3028     {
3029         if(
3030             !child_nodes ||
3031             child_nodes.length == 0
3032         ) {
3033             return;
3034         }
3035         
3036         var child_height = 0;
3037         
3038         for(var i = 0; i < child_nodes.length; i++) {
3039             
3040             // for modal with tabs...
3041             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3042                 
3043                 var layout_childs = child_nodes[i].childNodes;
3044                 
3045                 for(var j = 0; j < layout_childs.length; j++) {
3046                     
3047                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3048                         
3049                         var layout_body_childs = layout_childs[j].childNodes;
3050                         
3051                         for(var k = 0; k < layout_body_childs.length; k++) {
3052                             
3053                             if(layout_body_childs[k].classList.contains('navbar')) {
3054                                 child_height += layout_body_childs[k].offsetHeight;
3055                                 Roo.log('nav height: '+ layout_body_childs[k].offsetHeight);
3056                                 continue;
3057                             }
3058                             
3059                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3060                                 
3061                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3062                                 
3063                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3064                                     
3065                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3066                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3067                                         Roo.log('active panel height: '+this.getChildHeight(layout_body_tab_childs[m].childNodes));
3068                                         continue;
3069                                     }
3070                                     
3071                                 }
3072                                 
3073                             }
3074                             
3075                         }
3076                     }
3077                 }
3078                 continue;
3079             }
3080             
3081             child_height += child_nodes[i].offsetHeight;
3082         }
3083         
3084         return child_height;
3085     }
3086
3087 });
3088
3089
3090 Roo.apply(Roo.bootstrap.Modal,  {
3091     /**
3092          * Button config that displays a single OK button
3093          * @type Object
3094          */
3095         OK :  [{
3096             name : 'ok',
3097             weight : 'primary',
3098             html : 'OK'
3099         }],
3100         /**
3101          * Button config that displays Yes and No buttons
3102          * @type Object
3103          */
3104         YESNO : [
3105             {
3106                 name  : 'no',
3107                 html : 'No'
3108             },
3109             {
3110                 name  :'yes',
3111                 weight : 'primary',
3112                 html : 'Yes'
3113             }
3114         ],
3115
3116         /**
3117          * Button config that displays OK and Cancel buttons
3118          * @type Object
3119          */
3120         OKCANCEL : [
3121             {
3122                name : 'cancel',
3123                 html : 'Cancel'
3124             },
3125             {
3126                 name : 'ok',
3127                 weight : 'primary',
3128                 html : 'OK'
3129             }
3130         ],
3131         /**
3132          * Button config that displays Yes, No and Cancel buttons
3133          * @type Object
3134          */
3135         YESNOCANCEL : [
3136             {
3137                 name : 'yes',
3138                 weight : 'primary',
3139                 html : 'Yes'
3140             },
3141             {
3142                 name : 'no',
3143                 html : 'No'
3144             },
3145             {
3146                 name : 'cancel',
3147                 html : 'Cancel'
3148             }
3149         ],
3150         
3151         zIndex : 10001
3152 });
3153 /*
3154  * - LGPL
3155  *
3156  * messagebox - can be used as a replace
3157  * 
3158  */
3159 /**
3160  * @class Roo.MessageBox
3161  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3162  * Example usage:
3163  *<pre><code>
3164 // Basic alert:
3165 Roo.Msg.alert('Status', 'Changes saved successfully.');
3166
3167 // Prompt for user data:
3168 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3169     if (btn == 'ok'){
3170         // process text value...
3171     }
3172 });
3173
3174 // Show a dialog using config options:
3175 Roo.Msg.show({
3176    title:'Save Changes?',
3177    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3178    buttons: Roo.Msg.YESNOCANCEL,
3179    fn: processResult,
3180    animEl: 'elId'
3181 });
3182 </code></pre>
3183  * @singleton
3184  */
3185 Roo.bootstrap.MessageBox = function(){
3186     var dlg, opt, mask, waitTimer;
3187     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3188     var buttons, activeTextEl, bwidth;
3189
3190     
3191     // private
3192     var handleButton = function(button){
3193         dlg.hide();
3194         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3195     };
3196
3197     // private
3198     var handleHide = function(){
3199         if(opt && opt.cls){
3200             dlg.el.removeClass(opt.cls);
3201         }
3202         //if(waitTimer){
3203         //    Roo.TaskMgr.stop(waitTimer);
3204         //    waitTimer = null;
3205         //}
3206     };
3207
3208     // private
3209     var updateButtons = function(b){
3210         var width = 0;
3211         if(!b){
3212             buttons["ok"].hide();
3213             buttons["cancel"].hide();
3214             buttons["yes"].hide();
3215             buttons["no"].hide();
3216             //dlg.footer.dom.style.display = 'none';
3217             return width;
3218         }
3219         dlg.footerEl.dom.style.display = '';
3220         for(var k in buttons){
3221             if(typeof buttons[k] != "function"){
3222                 if(b[k]){
3223                     buttons[k].show();
3224                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3225                     width += buttons[k].el.getWidth()+15;
3226                 }else{
3227                     buttons[k].hide();
3228                 }
3229             }
3230         }
3231         return width;
3232     };
3233
3234     // private
3235     var handleEsc = function(d, k, e){
3236         if(opt && opt.closable !== false){
3237             dlg.hide();
3238         }
3239         if(e){
3240             e.stopEvent();
3241         }
3242     };
3243
3244     return {
3245         /**
3246          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3247          * @return {Roo.BasicDialog} The BasicDialog element
3248          */
3249         getDialog : function(){
3250            if(!dlg){
3251                 dlg = new Roo.bootstrap.Modal( {
3252                     //draggable: true,
3253                     //resizable:false,
3254                     //constraintoviewport:false,
3255                     //fixedcenter:true,
3256                     //collapsible : false,
3257                     //shim:true,
3258                     //modal: true,
3259                 //    width: 'auto',
3260                   //  height:100,
3261                     //buttonAlign:"center",
3262                     closeClick : function(){
3263                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3264                             handleButton("no");
3265                         }else{
3266                             handleButton("cancel");
3267                         }
3268                     }
3269                 });
3270                 dlg.render();
3271                 dlg.on("hide", handleHide);
3272                 mask = dlg.mask;
3273                 //dlg.addKeyListener(27, handleEsc);
3274                 buttons = {};
3275                 this.buttons = buttons;
3276                 var bt = this.buttonText;
3277                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3278                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3279                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3280                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3281                 //Roo.log(buttons);
3282                 bodyEl = dlg.bodyEl.createChild({
3283
3284                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3285                         '<textarea class="roo-mb-textarea"></textarea>' +
3286                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3287                 });
3288                 msgEl = bodyEl.dom.firstChild;
3289                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3290                 textboxEl.enableDisplayMode();
3291                 textboxEl.addKeyListener([10,13], function(){
3292                     if(dlg.isVisible() && opt && opt.buttons){
3293                         if(opt.buttons.ok){
3294                             handleButton("ok");
3295                         }else if(opt.buttons.yes){
3296                             handleButton("yes");
3297                         }
3298                     }
3299                 });
3300                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3301                 textareaEl.enableDisplayMode();
3302                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3303                 progressEl.enableDisplayMode();
3304                 
3305                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3306                 var pf = progressEl.dom.firstChild;
3307                 if (pf) {
3308                     pp = Roo.get(pf.firstChild);
3309                     pp.setHeight(pf.offsetHeight);
3310                 }
3311                 
3312             }
3313             return dlg;
3314         },
3315
3316         /**
3317          * Updates the message box body text
3318          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3319          * the XHTML-compliant non-breaking space character '&amp;#160;')
3320          * @return {Roo.MessageBox} This message box
3321          */
3322         updateText : function(text)
3323         {
3324             if(!dlg.isVisible() && !opt.width){
3325                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3326                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3327             }
3328             msgEl.innerHTML = text || '&#160;';
3329       
3330             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3331             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3332             var w = Math.max(
3333                     Math.min(opt.width || cw , this.maxWidth), 
3334                     Math.max(opt.minWidth || this.minWidth, bwidth)
3335             );
3336             if(opt.prompt){
3337                 activeTextEl.setWidth(w);
3338             }
3339             if(dlg.isVisible()){
3340                 dlg.fixedcenter = false;
3341             }
3342             // to big, make it scroll. = But as usual stupid IE does not support
3343             // !important..
3344             
3345             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3346                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3347                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3348             } else {
3349                 bodyEl.dom.style.height = '';
3350                 bodyEl.dom.style.overflowY = '';
3351             }
3352             if (cw > w) {
3353                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3354             } else {
3355                 bodyEl.dom.style.overflowX = '';
3356             }
3357             
3358             dlg.setContentSize(w, bodyEl.getHeight());
3359             if(dlg.isVisible()){
3360                 dlg.fixedcenter = true;
3361             }
3362             return this;
3363         },
3364
3365         /**
3366          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3367          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3368          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3369          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3370          * @return {Roo.MessageBox} This message box
3371          */
3372         updateProgress : function(value, text){
3373             if(text){
3374                 this.updateText(text);
3375             }
3376             
3377             if (pp) { // weird bug on my firefox - for some reason this is not defined
3378                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3379                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3380             }
3381             return this;
3382         },        
3383
3384         /**
3385          * Returns true if the message box is currently displayed
3386          * @return {Boolean} True if the message box is visible, else false
3387          */
3388         isVisible : function(){
3389             return dlg && dlg.isVisible();  
3390         },
3391
3392         /**
3393          * Hides the message box if it is displayed
3394          */
3395         hide : function(){
3396             if(this.isVisible()){
3397                 dlg.hide();
3398             }  
3399         },
3400
3401         /**
3402          * Displays a new message box, or reinitializes an existing message box, based on the config options
3403          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3404          * The following config object properties are supported:
3405          * <pre>
3406 Property    Type             Description
3407 ----------  ---------------  ------------------------------------------------------------------------------------
3408 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3409                                    closes (defaults to undefined)
3410 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3411                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3412 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3413                                    progress and wait dialogs will ignore this property and always hide the
3414                                    close button as they can only be closed programmatically.
3415 cls               String           A custom CSS class to apply to the message box element
3416 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3417                                    displayed (defaults to 75)
3418 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3419                                    function will be btn (the name of the button that was clicked, if applicable,
3420                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3421                                    Progress and wait dialogs will ignore this option since they do not respond to
3422                                    user actions and can only be closed programmatically, so any required function
3423                                    should be called by the same code after it closes the dialog.
3424 icon              String           A CSS class that provides a background image to be used as an icon for
3425                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3426 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3427 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3428 modal             Boolean          False to allow user interaction with the page while the message box is
3429                                    displayed (defaults to true)
3430 msg               String           A string that will replace the existing message box body text (defaults
3431                                    to the XHTML-compliant non-breaking space character '&#160;')
3432 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3433 progress          Boolean          True to display a progress bar (defaults to false)
3434 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3435 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3436 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3437 title             String           The title text
3438 value             String           The string value to set into the active textbox element if displayed
3439 wait              Boolean          True to display a progress bar (defaults to false)
3440 width             Number           The width of the dialog in pixels
3441 </pre>
3442          *
3443          * Example usage:
3444          * <pre><code>
3445 Roo.Msg.show({
3446    title: 'Address',
3447    msg: 'Please enter your address:',
3448    width: 300,
3449    buttons: Roo.MessageBox.OKCANCEL,
3450    multiline: true,
3451    fn: saveAddress,
3452    animEl: 'addAddressBtn'
3453 });
3454 </code></pre>
3455          * @param {Object} config Configuration options
3456          * @return {Roo.MessageBox} This message box
3457          */
3458         show : function(options)
3459         {
3460             
3461             // this causes nightmares if you show one dialog after another
3462             // especially on callbacks..
3463              
3464             if(this.isVisible()){
3465                 
3466                 this.hide();
3467                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3468                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3469                 Roo.log("New Dialog Message:" +  options.msg )
3470                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3471                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3472                 
3473             }
3474             var d = this.getDialog();
3475             opt = options;
3476             d.setTitle(opt.title || "&#160;");
3477             d.closeEl.setDisplayed(opt.closable !== false);
3478             activeTextEl = textboxEl;
3479             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3480             if(opt.prompt){
3481                 if(opt.multiline){
3482                     textboxEl.hide();
3483                     textareaEl.show();
3484                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3485                         opt.multiline : this.defaultTextHeight);
3486                     activeTextEl = textareaEl;
3487                 }else{
3488                     textboxEl.show();
3489                     textareaEl.hide();
3490                 }
3491             }else{
3492                 textboxEl.hide();
3493                 textareaEl.hide();
3494             }
3495             progressEl.setDisplayed(opt.progress === true);
3496             this.updateProgress(0);
3497             activeTextEl.dom.value = opt.value || "";
3498             if(opt.prompt){
3499                 dlg.setDefaultButton(activeTextEl);
3500             }else{
3501                 var bs = opt.buttons;
3502                 var db = null;
3503                 if(bs && bs.ok){
3504                     db = buttons["ok"];
3505                 }else if(bs && bs.yes){
3506                     db = buttons["yes"];
3507                 }
3508                 dlg.setDefaultButton(db);
3509             }
3510             bwidth = updateButtons(opt.buttons);
3511             this.updateText(opt.msg);
3512             if(opt.cls){
3513                 d.el.addClass(opt.cls);
3514             }
3515             d.proxyDrag = opt.proxyDrag === true;
3516             d.modal = opt.modal !== false;
3517             d.mask = opt.modal !== false ? mask : false;
3518             if(!d.isVisible()){
3519                 // force it to the end of the z-index stack so it gets a cursor in FF
3520                 document.body.appendChild(dlg.el.dom);
3521                 d.animateTarget = null;
3522                 d.show(options.animEl);
3523             }
3524             return this;
3525         },
3526
3527         /**
3528          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3529          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3530          * and closing the message box when the process is complete.
3531          * @param {String} title The title bar text
3532          * @param {String} msg The message box body text
3533          * @return {Roo.MessageBox} This message box
3534          */
3535         progress : function(title, msg){
3536             this.show({
3537                 title : title,
3538                 msg : msg,
3539                 buttons: false,
3540                 progress:true,
3541                 closable:false,
3542                 minWidth: this.minProgressWidth,
3543                 modal : true
3544             });
3545             return this;
3546         },
3547
3548         /**
3549          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3550          * If a callback function is passed it will be called after the user clicks the button, and the
3551          * id of the button that was clicked will be passed as the only parameter to the callback
3552          * (could also be the top-right close button).
3553          * @param {String} title The title bar text
3554          * @param {String} msg The message box body text
3555          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3556          * @param {Object} scope (optional) The scope of the callback function
3557          * @return {Roo.MessageBox} This message box
3558          */
3559         alert : function(title, msg, fn, scope)
3560         {
3561             this.show({
3562                 title : title,
3563                 msg : msg,
3564                 buttons: this.OK,
3565                 fn: fn,
3566                 closable : false,
3567                 scope : scope,
3568                 modal : true
3569             });
3570             return this;
3571         },
3572
3573         /**
3574          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3575          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3576          * You are responsible for closing the message box when the process is complete.
3577          * @param {String} msg The message box body text
3578          * @param {String} title (optional) The title bar text
3579          * @return {Roo.MessageBox} This message box
3580          */
3581         wait : function(msg, title){
3582             this.show({
3583                 title : title,
3584                 msg : msg,
3585                 buttons: false,
3586                 closable:false,
3587                 progress:true,
3588                 modal:true,
3589                 width:300,
3590                 wait:true
3591             });
3592             waitTimer = Roo.TaskMgr.start({
3593                 run: function(i){
3594                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3595                 },
3596                 interval: 1000
3597             });
3598             return this;
3599         },
3600
3601         /**
3602          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3603          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3604          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3605          * @param {String} title The title bar text
3606          * @param {String} msg The message box body text
3607          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3608          * @param {Object} scope (optional) The scope of the callback function
3609          * @return {Roo.MessageBox} This message box
3610          */
3611         confirm : function(title, msg, fn, scope){
3612             this.show({
3613                 title : title,
3614                 msg : msg,
3615                 buttons: this.YESNO,
3616                 fn: fn,
3617                 scope : scope,
3618                 modal : true
3619             });
3620             return this;
3621         },
3622
3623         /**
3624          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3625          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3626          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3627          * (could also be the top-right close button) and the text that was entered will be passed as the two
3628          * parameters to the callback.
3629          * @param {String} title The title bar text
3630          * @param {String} msg The message box body text
3631          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3632          * @param {Object} scope (optional) The scope of the callback function
3633          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3634          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3635          * @return {Roo.MessageBox} This message box
3636          */
3637         prompt : function(title, msg, fn, scope, multiline){
3638             this.show({
3639                 title : title,
3640                 msg : msg,
3641                 buttons: this.OKCANCEL,
3642                 fn: fn,
3643                 minWidth:250,
3644                 scope : scope,
3645                 prompt:true,
3646                 multiline: multiline,
3647                 modal : true
3648             });
3649             return this;
3650         },
3651
3652         /**
3653          * Button config that displays a single OK button
3654          * @type Object
3655          */
3656         OK : {ok:true},
3657         /**
3658          * Button config that displays Yes and No buttons
3659          * @type Object
3660          */
3661         YESNO : {yes:true, no:true},
3662         /**
3663          * Button config that displays OK and Cancel buttons
3664          * @type Object
3665          */
3666         OKCANCEL : {ok:true, cancel:true},
3667         /**
3668          * Button config that displays Yes, No and Cancel buttons
3669          * @type Object
3670          */
3671         YESNOCANCEL : {yes:true, no:true, cancel:true},
3672
3673         /**
3674          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3675          * @type Number
3676          */
3677         defaultTextHeight : 75,
3678         /**
3679          * The maximum width in pixels of the message box (defaults to 600)
3680          * @type Number
3681          */
3682         maxWidth : 600,
3683         /**
3684          * The minimum width in pixels of the message box (defaults to 100)
3685          * @type Number
3686          */
3687         minWidth : 100,
3688         /**
3689          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3690          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3691          * @type Number
3692          */
3693         minProgressWidth : 250,
3694         /**
3695          * An object containing the default button text strings that can be overriden for localized language support.
3696          * Supported properties are: ok, cancel, yes and no.
3697          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3698          * @type Object
3699          */
3700         buttonText : {
3701             ok : "OK",
3702             cancel : "Cancel",
3703             yes : "Yes",
3704             no : "No"
3705         }
3706     };
3707 }();
3708
3709 /**
3710  * Shorthand for {@link Roo.MessageBox}
3711  */
3712 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3713 Roo.Msg = Roo.Msg || Roo.MessageBox;
3714 /*
3715  * - LGPL
3716  *
3717  * navbar
3718  * 
3719  */
3720
3721 /**
3722  * @class Roo.bootstrap.Navbar
3723  * @extends Roo.bootstrap.Component
3724  * Bootstrap Navbar class
3725
3726  * @constructor
3727  * Create a new Navbar
3728  * @param {Object} config The config object
3729  */
3730
3731
3732 Roo.bootstrap.Navbar = function(config){
3733     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3734     this.addEvents({
3735         // raw events
3736         /**
3737          * @event beforetoggle
3738          * Fire before toggle the menu
3739          * @param {Roo.EventObject} e
3740          */
3741         "beforetoggle" : true
3742     });
3743 };
3744
3745 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3746     
3747     
3748    
3749     // private
3750     navItems : false,
3751     loadMask : false,
3752     
3753     
3754     getAutoCreate : function(){
3755         
3756         
3757         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3758         
3759     },
3760     
3761     initEvents :function ()
3762     {
3763         //Roo.log(this.el.select('.navbar-toggle',true));
3764         this.el.select('.navbar-toggle',true).on('click', function() {
3765             if(this.fireEvent('beforetoggle', this) !== false){
3766                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3767             }
3768             
3769         }, this);
3770         
3771         var mark = {
3772             tag: "div",
3773             cls:"x-dlg-mask"
3774         };
3775         
3776         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3777         
3778         var size = this.el.getSize();
3779         this.maskEl.setSize(size.width, size.height);
3780         this.maskEl.enableDisplayMode("block");
3781         this.maskEl.hide();
3782         
3783         if(this.loadMask){
3784             this.maskEl.show();
3785         }
3786     },
3787     
3788     
3789     getChildContainer : function()
3790     {
3791         if (this.el.select('.collapse').getCount()) {
3792             return this.el.select('.collapse',true).first();
3793         }
3794         
3795         return this.el;
3796     },
3797     
3798     mask : function()
3799     {
3800         this.maskEl.show();
3801     },
3802     
3803     unmask : function()
3804     {
3805         this.maskEl.hide();
3806     } 
3807     
3808     
3809     
3810     
3811 });
3812
3813
3814
3815  
3816
3817  /*
3818  * - LGPL
3819  *
3820  * navbar
3821  * 
3822  */
3823
3824 /**
3825  * @class Roo.bootstrap.NavSimplebar
3826  * @extends Roo.bootstrap.Navbar
3827  * Bootstrap Sidebar class
3828  *
3829  * @cfg {Boolean} inverse is inverted color
3830  * 
3831  * @cfg {String} type (nav | pills | tabs)
3832  * @cfg {Boolean} arrangement stacked | justified
3833  * @cfg {String} align (left | right) alignment
3834  * 
3835  * @cfg {Boolean} main (true|false) main nav bar? default false
3836  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3837  * 
3838  * @cfg {String} tag (header|footer|nav|div) default is nav 
3839
3840  * 
3841  * 
3842  * 
3843  * @constructor
3844  * Create a new Sidebar
3845  * @param {Object} config The config object
3846  */
3847
3848
3849 Roo.bootstrap.NavSimplebar = function(config){
3850     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3851 };
3852
3853 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3854     
3855     inverse: false,
3856     
3857     type: false,
3858     arrangement: '',
3859     align : false,
3860     
3861     
3862     
3863     main : false,
3864     
3865     
3866     tag : false,
3867     
3868     
3869     getAutoCreate : function(){
3870         
3871         
3872         var cfg = {
3873             tag : this.tag || 'div',
3874             cls : 'navbar'
3875         };
3876           
3877         
3878         cfg.cn = [
3879             {
3880                 cls: 'nav',
3881                 tag : 'ul'
3882             }
3883         ];
3884         
3885          
3886         this.type = this.type || 'nav';
3887         if (['tabs','pills'].indexOf(this.type)!==-1) {
3888             cfg.cn[0].cls += ' nav-' + this.type
3889         
3890         
3891         } else {
3892             if (this.type!=='nav') {
3893                 Roo.log('nav type must be nav/tabs/pills')
3894             }
3895             cfg.cn[0].cls += ' navbar-nav'
3896         }
3897         
3898         
3899         
3900         
3901         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3902             cfg.cn[0].cls += ' nav-' + this.arrangement;
3903         }
3904         
3905         
3906         if (this.align === 'right') {
3907             cfg.cn[0].cls += ' navbar-right';
3908         }
3909         
3910         if (this.inverse) {
3911             cfg.cls += ' navbar-inverse';
3912             
3913         }
3914         
3915         
3916         return cfg;
3917     
3918         
3919     }
3920     
3921     
3922     
3923 });
3924
3925
3926
3927  
3928
3929  
3930        /*
3931  * - LGPL
3932  *
3933  * navbar
3934  * 
3935  */
3936
3937 /**
3938  * @class Roo.bootstrap.NavHeaderbar
3939  * @extends Roo.bootstrap.NavSimplebar
3940  * Bootstrap Sidebar class
3941  *
3942  * @cfg {String} brand what is brand
3943  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3944  * @cfg {String} brand_href href of the brand
3945  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3946  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3947  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3948  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3949  * 
3950  * @constructor
3951  * Create a new Sidebar
3952  * @param {Object} config The config object
3953  */
3954
3955
3956 Roo.bootstrap.NavHeaderbar = function(config){
3957     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3958       
3959 };
3960
3961 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3962     
3963     position: '',
3964     brand: '',
3965     brand_href: false,
3966     srButton : true,
3967     autohide : false,
3968     desktopCenter : false,
3969    
3970     
3971     getAutoCreate : function(){
3972         
3973         var   cfg = {
3974             tag: this.nav || 'nav',
3975             cls: 'navbar',
3976             role: 'navigation',
3977             cn: []
3978         };
3979         
3980         var cn = cfg.cn;
3981         if (this.desktopCenter) {
3982             cn.push({cls : 'container', cn : []});
3983             cn = cn[0].cn;
3984         }
3985         
3986         if(this.srButton){
3987             cn.push({
3988                 tag: 'div',
3989                 cls: 'navbar-header',
3990                 cn: [
3991                     {
3992                         tag: 'button',
3993                         type: 'button',
3994                         cls: 'navbar-toggle',
3995                         'data-toggle': 'collapse',
3996                         cn: [
3997                             {
3998                                 tag: 'span',
3999                                 cls: 'sr-only',
4000                                 html: 'Toggle navigation'
4001                             },
4002                             {
4003                                 tag: 'span',
4004                                 cls: 'icon-bar'
4005                             },
4006                             {
4007                                 tag: 'span',
4008                                 cls: 'icon-bar'
4009                             },
4010                             {
4011                                 tag: 'span',
4012                                 cls: 'icon-bar'
4013                             }
4014                         ]
4015                     }
4016                 ]
4017             });
4018         }
4019         
4020         cn.push({
4021             tag: 'div',
4022             cls: 'collapse navbar-collapse',
4023             cn : []
4024         });
4025         
4026         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4027         
4028         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4029             cfg.cls += ' navbar-' + this.position;
4030             
4031             // tag can override this..
4032             
4033             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4034         }
4035         
4036         if (this.brand !== '') {
4037             cn[0].cn.push({
4038                 tag: 'a',
4039                 href: this.brand_href ? this.brand_href : '#',
4040                 cls: 'navbar-brand',
4041                 cn: [
4042                 this.brand
4043                 ]
4044             });
4045         }
4046         
4047         if(this.main){
4048             cfg.cls += ' main-nav';
4049         }
4050         
4051         
4052         return cfg;
4053
4054         
4055     },
4056     getHeaderChildContainer : function()
4057     {
4058         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4059             return this.el.select('.navbar-header',true).first();
4060         }
4061         
4062         return this.getChildContainer();
4063     },
4064     
4065     
4066     initEvents : function()
4067     {
4068         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4069         
4070         if (this.autohide) {
4071             
4072             var prevScroll = 0;
4073             var ft = this.el;
4074             
4075             Roo.get(document).on('scroll',function(e) {
4076                 var ns = Roo.get(document).getScroll().top;
4077                 var os = prevScroll;
4078                 prevScroll = ns;
4079                 
4080                 if(ns > os){
4081                     ft.removeClass('slideDown');
4082                     ft.addClass('slideUp');
4083                     return;
4084                 }
4085                 ft.removeClass('slideUp');
4086                 ft.addClass('slideDown');
4087                  
4088               
4089           },this);
4090         }
4091     }    
4092     
4093 });
4094
4095
4096
4097  
4098
4099  /*
4100  * - LGPL
4101  *
4102  * navbar
4103  * 
4104  */
4105
4106 /**
4107  * @class Roo.bootstrap.NavSidebar
4108  * @extends Roo.bootstrap.Navbar
4109  * Bootstrap Sidebar class
4110  * 
4111  * @constructor
4112  * Create a new Sidebar
4113  * @param {Object} config The config object
4114  */
4115
4116
4117 Roo.bootstrap.NavSidebar = function(config){
4118     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4119 };
4120
4121 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4122     
4123     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4124     
4125     getAutoCreate : function(){
4126         
4127         
4128         return  {
4129             tag: 'div',
4130             cls: 'sidebar sidebar-nav'
4131         };
4132     
4133         
4134     }
4135     
4136     
4137     
4138 });
4139
4140
4141
4142  
4143
4144  /*
4145  * - LGPL
4146  *
4147  * nav group
4148  * 
4149  */
4150
4151 /**
4152  * @class Roo.bootstrap.NavGroup
4153  * @extends Roo.bootstrap.Component
4154  * Bootstrap NavGroup class
4155  * @cfg {String} align (left|right)
4156  * @cfg {Boolean} inverse
4157  * @cfg {String} type (nav|pills|tab) default nav
4158  * @cfg {String} navId - reference Id for navbar.
4159
4160  * 
4161  * @constructor
4162  * Create a new nav group
4163  * @param {Object} config The config object
4164  */
4165
4166 Roo.bootstrap.NavGroup = function(config){
4167     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4168     this.navItems = [];
4169    
4170     Roo.bootstrap.NavGroup.register(this);
4171      this.addEvents({
4172         /**
4173              * @event changed
4174              * Fires when the active item changes
4175              * @param {Roo.bootstrap.NavGroup} this
4176              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4177              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4178          */
4179         'changed': true
4180      });
4181     
4182 };
4183
4184 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4185     
4186     align: '',
4187     inverse: false,
4188     form: false,
4189     type: 'nav',
4190     navId : '',
4191     // private
4192     
4193     navItems : false, 
4194     
4195     getAutoCreate : function()
4196     {
4197         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4198         
4199         cfg = {
4200             tag : 'ul',
4201             cls: 'nav' 
4202         };
4203         
4204         if (['tabs','pills'].indexOf(this.type)!==-1) {
4205             cfg.cls += ' nav-' + this.type
4206         } else {
4207             if (this.type!=='nav') {
4208                 Roo.log('nav type must be nav/tabs/pills')
4209             }
4210             cfg.cls += ' navbar-nav'
4211         }
4212         
4213         if (this.parent() && this.parent().sidebar) {
4214             cfg = {
4215                 tag: 'ul',
4216                 cls: 'dashboard-menu sidebar-menu'
4217             };
4218             
4219             return cfg;
4220         }
4221         
4222         if (this.form === true) {
4223             cfg = {
4224                 tag: 'form',
4225                 cls: 'navbar-form'
4226             };
4227             
4228             if (this.align === 'right') {
4229                 cfg.cls += ' navbar-right';
4230             } else {
4231                 cfg.cls += ' navbar-left';
4232             }
4233         }
4234         
4235         if (this.align === 'right') {
4236             cfg.cls += ' navbar-right';
4237         }
4238         
4239         if (this.inverse) {
4240             cfg.cls += ' navbar-inverse';
4241             
4242         }
4243         
4244         
4245         return cfg;
4246     },
4247     /**
4248     * sets the active Navigation item
4249     * @param {Roo.bootstrap.NavItem} the new current navitem
4250     */
4251     setActiveItem : function(item)
4252     {
4253         var prev = false;
4254         Roo.each(this.navItems, function(v){
4255             if (v == item) {
4256                 return ;
4257             }
4258             if (v.isActive()) {
4259                 v.setActive(false, true);
4260                 prev = v;
4261                 
4262             }
4263             
4264         });
4265
4266         item.setActive(true, true);
4267         this.fireEvent('changed', this, item, prev);
4268         
4269         
4270     },
4271     /**
4272     * gets the active Navigation item
4273     * @return {Roo.bootstrap.NavItem} the current navitem
4274     */
4275     getActive : function()
4276     {
4277         
4278         var prev = false;
4279         Roo.each(this.navItems, function(v){
4280             
4281             if (v.isActive()) {
4282                 prev = v;
4283                 
4284             }
4285             
4286         });
4287         return prev;
4288     },
4289     
4290     indexOfNav : function()
4291     {
4292         
4293         var prev = false;
4294         Roo.each(this.navItems, function(v,i){
4295             
4296             if (v.isActive()) {
4297                 prev = i;
4298                 
4299             }
4300             
4301         });
4302         return prev;
4303     },
4304     /**
4305     * adds a Navigation item
4306     * @param {Roo.bootstrap.NavItem} the navitem to add
4307     */
4308     addItem : function(cfg)
4309     {
4310         var cn = new Roo.bootstrap.NavItem(cfg);
4311         this.register(cn);
4312         cn.parentId = this.id;
4313         cn.onRender(this.el, null);
4314         return cn;
4315     },
4316     /**
4317     * register a Navigation item
4318     * @param {Roo.bootstrap.NavItem} the navitem to add
4319     */
4320     register : function(item)
4321     {
4322         this.navItems.push( item);
4323         item.navId = this.navId;
4324     
4325     },
4326     
4327     /**
4328     * clear all the Navigation item
4329     */
4330    
4331     clearAll : function()
4332     {
4333         this.navItems = [];
4334         this.el.dom.innerHTML = '';
4335     },
4336     
4337     getNavItem: function(tabId)
4338     {
4339         var ret = false;
4340         Roo.each(this.navItems, function(e) {
4341             if (e.tabId == tabId) {
4342                ret =  e;
4343                return false;
4344             }
4345             return true;
4346             
4347         });
4348         return ret;
4349     },
4350     
4351     setActiveNext : function()
4352     {
4353         var i = this.indexOfNav(this.getActive());
4354         if (i > this.navItems.length) {
4355             return;
4356         }
4357         this.setActiveItem(this.navItems[i+1]);
4358     },
4359     setActivePrev : function()
4360     {
4361         var i = this.indexOfNav(this.getActive());
4362         if (i  < 1) {
4363             return;
4364         }
4365         this.setActiveItem(this.navItems[i-1]);
4366     },
4367     clearWasActive : function(except) {
4368         Roo.each(this.navItems, function(e) {
4369             if (e.tabId != except.tabId && e.was_active) {
4370                e.was_active = false;
4371                return false;
4372             }
4373             return true;
4374             
4375         });
4376     },
4377     getWasActive : function ()
4378     {
4379         var r = false;
4380         Roo.each(this.navItems, function(e) {
4381             if (e.was_active) {
4382                r = e;
4383                return false;
4384             }
4385             return true;
4386             
4387         });
4388         return r;
4389     }
4390     
4391     
4392 });
4393
4394  
4395 Roo.apply(Roo.bootstrap.NavGroup, {
4396     
4397     groups: {},
4398      /**
4399     * register a Navigation Group
4400     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4401     */
4402     register : function(navgrp)
4403     {
4404         this.groups[navgrp.navId] = navgrp;
4405         
4406     },
4407     /**
4408     * fetch a Navigation Group based on the navigation ID
4409     * @param {string} the navgroup to add
4410     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4411     */
4412     get: function(navId) {
4413         if (typeof(this.groups[navId]) == 'undefined') {
4414             return false;
4415             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4416         }
4417         return this.groups[navId] ;
4418     }
4419     
4420     
4421     
4422 });
4423
4424  /*
4425  * - LGPL
4426  *
4427  * row
4428  * 
4429  */
4430
4431 /**
4432  * @class Roo.bootstrap.NavItem
4433  * @extends Roo.bootstrap.Component
4434  * Bootstrap Navbar.NavItem class
4435  * @cfg {String} href  link to
4436  * @cfg {String} html content of button
4437  * @cfg {String} badge text inside badge
4438  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4439  * @cfg {String} glyphicon name of glyphicon
4440  * @cfg {String} icon name of font awesome icon
4441  * @cfg {Boolean} active Is item active
4442  * @cfg {Boolean} disabled Is item disabled
4443  
4444  * @cfg {Boolean} preventDefault (true | false) default false
4445  * @cfg {String} tabId the tab that this item activates.
4446  * @cfg {String} tagtype (a|span) render as a href or span?
4447  * @cfg {Boolean} animateRef (true|false) link to element default false  
4448   
4449  * @constructor
4450  * Create a new Navbar Item
4451  * @param {Object} config The config object
4452  */
4453 Roo.bootstrap.NavItem = function(config){
4454     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4455     this.addEvents({
4456         // raw events
4457         /**
4458          * @event click
4459          * The raw click event for the entire grid.
4460          * @param {Roo.EventObject} e
4461          */
4462         "click" : true,
4463          /**
4464             * @event changed
4465             * Fires when the active item active state changes
4466             * @param {Roo.bootstrap.NavItem} this
4467             * @param {boolean} state the new state
4468              
4469          */
4470         'changed': true,
4471         /**
4472             * @event scrollto
4473             * Fires when scroll to element
4474             * @param {Roo.bootstrap.NavItem} this
4475             * @param {Object} options
4476             * @param {Roo.EventObject} e
4477              
4478          */
4479         'scrollto': true
4480     });
4481    
4482 };
4483
4484 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4485     
4486     href: false,
4487     html: '',
4488     badge: '',
4489     icon: false,
4490     glyphicon: false,
4491     active: false,
4492     preventDefault : false,
4493     tabId : false,
4494     tagtype : 'a',
4495     disabled : false,
4496     animateRef : false,
4497     was_active : false,
4498     
4499     getAutoCreate : function(){
4500          
4501         var cfg = {
4502             tag: 'li',
4503             cls: 'nav-item'
4504             
4505         };
4506         
4507         if (this.active) {
4508             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4509         }
4510         if (this.disabled) {
4511             cfg.cls += ' disabled';
4512         }
4513         
4514         if (this.href || this.html || this.glyphicon || this.icon) {
4515             cfg.cn = [
4516                 {
4517                     tag: this.tagtype,
4518                     href : this.href || "#",
4519                     html: this.html || ''
4520                 }
4521             ];
4522             
4523             if (this.icon) {
4524                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4525             }
4526
4527             if(this.glyphicon) {
4528                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4529             }
4530             
4531             if (this.menu) {
4532                 
4533                 cfg.cn[0].html += " <span class='caret'></span>";
4534              
4535             }
4536             
4537             if (this.badge !== '') {
4538                  
4539                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4540             }
4541         }
4542         
4543         
4544         
4545         return cfg;
4546     },
4547     initEvents: function() 
4548     {
4549         if (typeof (this.menu) != 'undefined') {
4550             this.menu.parentType = this.xtype;
4551             this.menu.triggerEl = this.el;
4552             this.menu = this.addxtype(Roo.apply({}, this.menu));
4553         }
4554         
4555         this.el.select('a',true).on('click', this.onClick, this);
4556         
4557         if(this.tagtype == 'span'){
4558             this.el.select('span',true).on('click', this.onClick, this);
4559         }
4560        
4561         // at this point parent should be available..
4562         this.parent().register(this);
4563     },
4564     
4565     onClick : function(e)
4566     {
4567         if (e.getTarget('.dropdown-menu-item')) {
4568             // did you click on a menu itemm.... - then don't trigger onclick..
4569             return;
4570         }
4571         
4572         if(
4573                 this.preventDefault || 
4574                 this.href == '#' 
4575         ){
4576             Roo.log("NavItem - prevent Default?");
4577             e.preventDefault();
4578         }
4579         
4580         if (this.disabled) {
4581             return;
4582         }
4583         
4584         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4585         if (tg && tg.transition) {
4586             Roo.log("waiting for the transitionend");
4587             return;
4588         }
4589         
4590         
4591         
4592         //Roo.log("fire event clicked");
4593         if(this.fireEvent('click', this, e) === false){
4594             return;
4595         };
4596         
4597         if(this.tagtype == 'span'){
4598             return;
4599         }
4600         
4601         //Roo.log(this.href);
4602         var ael = this.el.select('a',true).first();
4603         //Roo.log(ael);
4604         
4605         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4606             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4607             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4608                 return; // ignore... - it's a 'hash' to another page.
4609             }
4610             Roo.log("NavItem - prevent Default?");
4611             e.preventDefault();
4612             this.scrollToElement(e);
4613         }
4614         
4615         
4616         var p =  this.parent();
4617    
4618         if (['tabs','pills'].indexOf(p.type)!==-1) {
4619             if (typeof(p.setActiveItem) !== 'undefined') {
4620                 p.setActiveItem(this);
4621             }
4622         }
4623         
4624         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4625         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4626             // remove the collapsed menu expand...
4627             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4628         }
4629     },
4630     
4631     isActive: function () {
4632         return this.active
4633     },
4634     setActive : function(state, fire, is_was_active)
4635     {
4636         if (this.active && !state && this.navId) {
4637             this.was_active = true;
4638             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4639             if (nv) {
4640                 nv.clearWasActive(this);
4641             }
4642             
4643         }
4644         this.active = state;
4645         
4646         if (!state ) {
4647             this.el.removeClass('active');
4648         } else if (!this.el.hasClass('active')) {
4649             this.el.addClass('active');
4650         }
4651         if (fire) {
4652             this.fireEvent('changed', this, state);
4653         }
4654         
4655         // show a panel if it's registered and related..
4656         
4657         if (!this.navId || !this.tabId || !state || is_was_active) {
4658             return;
4659         }
4660         
4661         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4662         if (!tg) {
4663             return;
4664         }
4665         var pan = tg.getPanelByName(this.tabId);
4666         if (!pan) {
4667             return;
4668         }
4669         // if we can not flip to new panel - go back to old nav highlight..
4670         if (false == tg.showPanel(pan)) {
4671             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4672             if (nv) {
4673                 var onav = nv.getWasActive();
4674                 if (onav) {
4675                     onav.setActive(true, false, true);
4676                 }
4677             }
4678             
4679         }
4680         
4681         
4682         
4683     },
4684      // this should not be here...
4685     setDisabled : function(state)
4686     {
4687         this.disabled = state;
4688         if (!state ) {
4689             this.el.removeClass('disabled');
4690         } else if (!this.el.hasClass('disabled')) {
4691             this.el.addClass('disabled');
4692         }
4693         
4694     },
4695     
4696     /**
4697      * Fetch the element to display the tooltip on.
4698      * @return {Roo.Element} defaults to this.el
4699      */
4700     tooltipEl : function()
4701     {
4702         return this.el.select('' + this.tagtype + '', true).first();
4703     },
4704     
4705     scrollToElement : function(e)
4706     {
4707         var c = document.body;
4708         
4709         /*
4710          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4711          */
4712         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4713             c = document.documentElement;
4714         }
4715         
4716         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4717         
4718         if(!target){
4719             return;
4720         }
4721
4722         var o = target.calcOffsetsTo(c);
4723         
4724         var options = {
4725             target : target,
4726             value : o[1]
4727         };
4728         
4729         this.fireEvent('scrollto', this, options, e);
4730         
4731         Roo.get(c).scrollTo('top', options.value, true);
4732         
4733         return;
4734     }
4735 });
4736  
4737
4738  /*
4739  * - LGPL
4740  *
4741  * sidebar item
4742  *
4743  *  li
4744  *    <span> icon </span>
4745  *    <span> text </span>
4746  *    <span>badge </span>
4747  */
4748
4749 /**
4750  * @class Roo.bootstrap.NavSidebarItem
4751  * @extends Roo.bootstrap.NavItem
4752  * Bootstrap Navbar.NavSidebarItem class
4753  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4754  * {Boolean} open is the menu open
4755  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4756  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4757  * {String} buttonSize (sm|md|lg)the extra classes for the button
4758  * {Boolean} showArrow show arrow next to the text (default true)
4759  * @constructor
4760  * Create a new Navbar Button
4761  * @param {Object} config The config object
4762  */
4763 Roo.bootstrap.NavSidebarItem = function(config){
4764     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4765     this.addEvents({
4766         // raw events
4767         /**
4768          * @event click
4769          * The raw click event for the entire grid.
4770          * @param {Roo.EventObject} e
4771          */
4772         "click" : true,
4773          /**
4774             * @event changed
4775             * Fires when the active item active state changes
4776             * @param {Roo.bootstrap.NavSidebarItem} this
4777             * @param {boolean} state the new state
4778              
4779          */
4780         'changed': true
4781     });
4782    
4783 };
4784
4785 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4786     
4787     badgeWeight : 'default',
4788     
4789     open: false,
4790     
4791     buttonView : false,
4792     
4793     buttonWeight : 'default',
4794     
4795     buttonSize : 'md',
4796     
4797     showArrow : true,
4798     
4799     getAutoCreate : function(){
4800         
4801         
4802         var a = {
4803                 tag: 'a',
4804                 href : this.href || '#',
4805                 cls: '',
4806                 html : '',
4807                 cn : []
4808         };
4809         
4810         if(this.buttonView){
4811             a = {
4812                 tag: 'button',
4813                 href : this.href || '#',
4814                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4815                 html : this.html,
4816                 cn : []
4817             };
4818         }
4819         
4820         var cfg = {
4821             tag: 'li',
4822             cls: '',
4823             cn: [ a ]
4824         };
4825         
4826         if (this.active) {
4827             cfg.cls += ' active';
4828         }
4829         
4830         if (this.disabled) {
4831             cfg.cls += ' disabled';
4832         }
4833         if (this.open) {
4834             cfg.cls += ' open x-open';
4835         }
4836         // left icon..
4837         if (this.glyphicon || this.icon) {
4838             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4839             a.cn.push({ tag : 'i', cls : c }) ;
4840         }
4841         
4842         if(!this.buttonView){
4843             var span = {
4844                 tag: 'span',
4845                 html : this.html || ''
4846             };
4847
4848             a.cn.push(span);
4849             
4850         }
4851         
4852         if (this.badge !== '') {
4853             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4854         }
4855         
4856         if (this.menu) {
4857             
4858             if(this.showArrow){
4859                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4860             }
4861             
4862             a.cls += ' dropdown-toggle treeview' ;
4863         }
4864         
4865         return cfg;
4866     },
4867     
4868     initEvents : function()
4869     { 
4870         if (typeof (this.menu) != 'undefined') {
4871             this.menu.parentType = this.xtype;
4872             this.menu.triggerEl = this.el;
4873             this.menu = this.addxtype(Roo.apply({}, this.menu));
4874         }
4875         
4876         this.el.on('click', this.onClick, this);
4877         
4878         if(this.badge !== ''){
4879             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4880         }
4881         
4882     },
4883     
4884     onClick : function(e)
4885     {
4886         if(this.disabled){
4887             e.preventDefault();
4888             return;
4889         }
4890         
4891         if(this.preventDefault){
4892             e.preventDefault();
4893         }
4894         
4895         this.fireEvent('click', this);
4896     },
4897     
4898     disable : function()
4899     {
4900         this.setDisabled(true);
4901     },
4902     
4903     enable : function()
4904     {
4905         this.setDisabled(false);
4906     },
4907     
4908     setDisabled : function(state)
4909     {
4910         if(this.disabled == state){
4911             return;
4912         }
4913         
4914         this.disabled = state;
4915         
4916         if (state) {
4917             this.el.addClass('disabled');
4918             return;
4919         }
4920         
4921         this.el.removeClass('disabled');
4922         
4923         return;
4924     },
4925     
4926     setActive : function(state)
4927     {
4928         if(this.active == state){
4929             return;
4930         }
4931         
4932         this.active = state;
4933         
4934         if (state) {
4935             this.el.addClass('active');
4936             return;
4937         }
4938         
4939         this.el.removeClass('active');
4940         
4941         return;
4942     },
4943     
4944     isActive: function () 
4945     {
4946         return this.active;
4947     },
4948     
4949     setBadge : function(str)
4950     {
4951         if(!this.badgeEl){
4952             return;
4953         }
4954         
4955         this.badgeEl.dom.innerHTML = str;
4956     }
4957     
4958    
4959      
4960  
4961 });
4962  
4963
4964  /*
4965  * - LGPL
4966  *
4967  * row
4968  * 
4969  */
4970
4971 /**
4972  * @class Roo.bootstrap.Row
4973  * @extends Roo.bootstrap.Component
4974  * Bootstrap Row class (contains columns...)
4975  * 
4976  * @constructor
4977  * Create a new Row
4978  * @param {Object} config The config object
4979  */
4980
4981 Roo.bootstrap.Row = function(config){
4982     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4983 };
4984
4985 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4986     
4987     getAutoCreate : function(){
4988        return {
4989             cls: 'row clearfix'
4990        };
4991     }
4992     
4993     
4994 });
4995
4996  
4997
4998  /*
4999  * - LGPL
5000  *
5001  * element
5002  * 
5003  */
5004
5005 /**
5006  * @class Roo.bootstrap.Element
5007  * @extends Roo.bootstrap.Component
5008  * Bootstrap Element class
5009  * @cfg {String} html contents of the element
5010  * @cfg {String} tag tag of the element
5011  * @cfg {String} cls class of the element
5012  * @cfg {Boolean} preventDefault (true|false) default false
5013  * @cfg {Boolean} clickable (true|false) default false
5014  * 
5015  * @constructor
5016  * Create a new Element
5017  * @param {Object} config The config object
5018  */
5019
5020 Roo.bootstrap.Element = function(config){
5021     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5022     
5023     this.addEvents({
5024         // raw events
5025         /**
5026          * @event click
5027          * When a element is chick
5028          * @param {Roo.bootstrap.Element} this
5029          * @param {Roo.EventObject} e
5030          */
5031         "click" : true
5032     });
5033 };
5034
5035 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5036     
5037     tag: 'div',
5038     cls: '',
5039     html: '',
5040     preventDefault: false, 
5041     clickable: false,
5042     
5043     getAutoCreate : function(){
5044         
5045         var cfg = {
5046             tag: this.tag,
5047             // cls: this.cls, double assign in parent class Component.js :: onRender
5048             html: this.html
5049         };
5050         
5051         return cfg;
5052     },
5053     
5054     initEvents: function() 
5055     {
5056         Roo.bootstrap.Element.superclass.initEvents.call(this);
5057         
5058         if(this.clickable){
5059             this.el.on('click', this.onClick, this);
5060         }
5061         
5062     },
5063     
5064     onClick : function(e)
5065     {
5066         if(this.preventDefault){
5067             e.preventDefault();
5068         }
5069         
5070         this.fireEvent('click', this, e);
5071     },
5072     
5073     getValue : function()
5074     {
5075         return this.el.dom.innerHTML;
5076     },
5077     
5078     setValue : function(value)
5079     {
5080         this.el.dom.innerHTML = value;
5081     }
5082    
5083 });
5084
5085  
5086
5087  /*
5088  * - LGPL
5089  *
5090  * pagination
5091  * 
5092  */
5093
5094 /**
5095  * @class Roo.bootstrap.Pagination
5096  * @extends Roo.bootstrap.Component
5097  * Bootstrap Pagination class
5098  * @cfg {String} size xs | sm | md | lg
5099  * @cfg {Boolean} inverse false | true
5100  * 
5101  * @constructor
5102  * Create a new Pagination
5103  * @param {Object} config The config object
5104  */
5105
5106 Roo.bootstrap.Pagination = function(config){
5107     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5108 };
5109
5110 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5111     
5112     cls: false,
5113     size: false,
5114     inverse: false,
5115     
5116     getAutoCreate : function(){
5117         var cfg = {
5118             tag: 'ul',
5119                 cls: 'pagination'
5120         };
5121         if (this.inverse) {
5122             cfg.cls += ' inverse';
5123         }
5124         if (this.html) {
5125             cfg.html=this.html;
5126         }
5127         if (this.cls) {
5128             cfg.cls += " " + this.cls;
5129         }
5130         return cfg;
5131     }
5132    
5133 });
5134
5135  
5136
5137  /*
5138  * - LGPL
5139  *
5140  * Pagination item
5141  * 
5142  */
5143
5144
5145 /**
5146  * @class Roo.bootstrap.PaginationItem
5147  * @extends Roo.bootstrap.Component
5148  * Bootstrap PaginationItem class
5149  * @cfg {String} html text
5150  * @cfg {String} href the link
5151  * @cfg {Boolean} preventDefault (true | false) default true
5152  * @cfg {Boolean} active (true | false) default false
5153  * @cfg {Boolean} disabled default false
5154  * 
5155  * 
5156  * @constructor
5157  * Create a new PaginationItem
5158  * @param {Object} config The config object
5159  */
5160
5161
5162 Roo.bootstrap.PaginationItem = function(config){
5163     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5164     this.addEvents({
5165         // raw events
5166         /**
5167          * @event click
5168          * The raw click event for the entire grid.
5169          * @param {Roo.EventObject} e
5170          */
5171         "click" : true
5172     });
5173 };
5174
5175 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5176     
5177     href : false,
5178     html : false,
5179     preventDefault: true,
5180     active : false,
5181     cls : false,
5182     disabled: false,
5183     
5184     getAutoCreate : function(){
5185         var cfg= {
5186             tag: 'li',
5187             cn: [
5188                 {
5189                     tag : 'a',
5190                     href : this.href ? this.href : '#',
5191                     html : this.html ? this.html : ''
5192                 }
5193             ]
5194         };
5195         
5196         if(this.cls){
5197             cfg.cls = this.cls;
5198         }
5199         
5200         if(this.disabled){
5201             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5202         }
5203         
5204         if(this.active){
5205             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5206         }
5207         
5208         return cfg;
5209     },
5210     
5211     initEvents: function() {
5212         
5213         this.el.on('click', this.onClick, this);
5214         
5215     },
5216     onClick : function(e)
5217     {
5218         Roo.log('PaginationItem on click ');
5219         if(this.preventDefault){
5220             e.preventDefault();
5221         }
5222         
5223         if(this.disabled){
5224             return;
5225         }
5226         
5227         this.fireEvent('click', this, e);
5228     }
5229    
5230 });
5231
5232  
5233
5234  /*
5235  * - LGPL
5236  *
5237  * slider
5238  * 
5239  */
5240
5241
5242 /**
5243  * @class Roo.bootstrap.Slider
5244  * @extends Roo.bootstrap.Component
5245  * Bootstrap Slider class
5246  *    
5247  * @constructor
5248  * Create a new Slider
5249  * @param {Object} config The config object
5250  */
5251
5252 Roo.bootstrap.Slider = function(config){
5253     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5254 };
5255
5256 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5257     
5258     getAutoCreate : function(){
5259         
5260         var cfg = {
5261             tag: 'div',
5262             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5263             cn: [
5264                 {
5265                     tag: 'a',
5266                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5267                 }
5268             ]
5269         };
5270         
5271         return cfg;
5272     }
5273    
5274 });
5275
5276  /*
5277  * Based on:
5278  * Ext JS Library 1.1.1
5279  * Copyright(c) 2006-2007, Ext JS, LLC.
5280  *
5281  * Originally Released Under LGPL - original licence link has changed is not relivant.
5282  *
5283  * Fork - LGPL
5284  * <script type="text/javascript">
5285  */
5286  
5287
5288 /**
5289  * @class Roo.grid.ColumnModel
5290  * @extends Roo.util.Observable
5291  * This is the default implementation of a ColumnModel used by the Grid. It defines
5292  * the columns in the grid.
5293  * <br>Usage:<br>
5294  <pre><code>
5295  var colModel = new Roo.grid.ColumnModel([
5296         {header: "Ticker", width: 60, sortable: true, locked: true},
5297         {header: "Company Name", width: 150, sortable: true},
5298         {header: "Market Cap.", width: 100, sortable: true},
5299         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5300         {header: "Employees", width: 100, sortable: true, resizable: false}
5301  ]);
5302  </code></pre>
5303  * <p>
5304  
5305  * The config options listed for this class are options which may appear in each
5306  * individual column definition.
5307  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5308  * @constructor
5309  * @param {Object} config An Array of column config objects. See this class's
5310  * config objects for details.
5311 */
5312 Roo.grid.ColumnModel = function(config){
5313         /**
5314      * The config passed into the constructor
5315      */
5316     this.config = config;
5317     this.lookup = {};
5318
5319     // if no id, create one
5320     // if the column does not have a dataIndex mapping,
5321     // map it to the order it is in the config
5322     for(var i = 0, len = config.length; i < len; i++){
5323         var c = config[i];
5324         if(typeof c.dataIndex == "undefined"){
5325             c.dataIndex = i;
5326         }
5327         if(typeof c.renderer == "string"){
5328             c.renderer = Roo.util.Format[c.renderer];
5329         }
5330         if(typeof c.id == "undefined"){
5331             c.id = Roo.id();
5332         }
5333         if(c.editor && c.editor.xtype){
5334             c.editor  = Roo.factory(c.editor, Roo.grid);
5335         }
5336         if(c.editor && c.editor.isFormField){
5337             c.editor = new Roo.grid.GridEditor(c.editor);
5338         }
5339         this.lookup[c.id] = c;
5340     }
5341
5342     /**
5343      * The width of columns which have no width specified (defaults to 100)
5344      * @type Number
5345      */
5346     this.defaultWidth = 100;
5347
5348     /**
5349      * Default sortable of columns which have no sortable specified (defaults to false)
5350      * @type Boolean
5351      */
5352     this.defaultSortable = false;
5353
5354     this.addEvents({
5355         /**
5356              * @event widthchange
5357              * Fires when the width of a column changes.
5358              * @param {ColumnModel} this
5359              * @param {Number} columnIndex The column index
5360              * @param {Number} newWidth The new width
5361              */
5362             "widthchange": true,
5363         /**
5364              * @event headerchange
5365              * Fires when the text of a header changes.
5366              * @param {ColumnModel} this
5367              * @param {Number} columnIndex The column index
5368              * @param {Number} newText The new header text
5369              */
5370             "headerchange": true,
5371         /**
5372              * @event hiddenchange
5373              * Fires when a column is hidden or "unhidden".
5374              * @param {ColumnModel} this
5375              * @param {Number} columnIndex The column index
5376              * @param {Boolean} hidden true if hidden, false otherwise
5377              */
5378             "hiddenchange": true,
5379             /**
5380          * @event columnmoved
5381          * Fires when a column is moved.
5382          * @param {ColumnModel} this
5383          * @param {Number} oldIndex
5384          * @param {Number} newIndex
5385          */
5386         "columnmoved" : true,
5387         /**
5388          * @event columlockchange
5389          * Fires when a column's locked state is changed
5390          * @param {ColumnModel} this
5391          * @param {Number} colIndex
5392          * @param {Boolean} locked true if locked
5393          */
5394         "columnlockchange" : true
5395     });
5396     Roo.grid.ColumnModel.superclass.constructor.call(this);
5397 };
5398 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5399     /**
5400      * @cfg {String} header The header text to display in the Grid view.
5401      */
5402     /**
5403      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5404      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5405      * specified, the column's index is used as an index into the Record's data Array.
5406      */
5407     /**
5408      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5409      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5410      */
5411     /**
5412      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5413      * Defaults to the value of the {@link #defaultSortable} property.
5414      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5415      */
5416     /**
5417      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5418      */
5419     /**
5420      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5421      */
5422     /**
5423      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5424      */
5425     /**
5426      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5427      */
5428     /**
5429      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5430      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5431      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5432      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5433      */
5434        /**
5435      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5436      */
5437     /**
5438      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5439      */
5440     /**
5441      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5442      */
5443     /**
5444      * @cfg {String} cursor (Optional)
5445      */
5446     /**
5447      * @cfg {String} tooltip (Optional)
5448      */
5449     /**
5450      * @cfg {Number} xs (Optional)
5451      */
5452     /**
5453      * @cfg {Number} sm (Optional)
5454      */
5455     /**
5456      * @cfg {Number} md (Optional)
5457      */
5458     /**
5459      * @cfg {Number} lg (Optional)
5460      */
5461     /**
5462      * Returns the id of the column at the specified index.
5463      * @param {Number} index The column index
5464      * @return {String} the id
5465      */
5466     getColumnId : function(index){
5467         return this.config[index].id;
5468     },
5469
5470     /**
5471      * Returns the column for a specified id.
5472      * @param {String} id The column id
5473      * @return {Object} the column
5474      */
5475     getColumnById : function(id){
5476         return this.lookup[id];
5477     },
5478
5479     
5480     /**
5481      * Returns the column for a specified dataIndex.
5482      * @param {String} dataIndex The column dataIndex
5483      * @return {Object|Boolean} the column or false if not found
5484      */
5485     getColumnByDataIndex: function(dataIndex){
5486         var index = this.findColumnIndex(dataIndex);
5487         return index > -1 ? this.config[index] : false;
5488     },
5489     
5490     /**
5491      * Returns the index for a specified column id.
5492      * @param {String} id The column id
5493      * @return {Number} the index, or -1 if not found
5494      */
5495     getIndexById : function(id){
5496         for(var i = 0, len = this.config.length; i < len; i++){
5497             if(this.config[i].id == id){
5498                 return i;
5499             }
5500         }
5501         return -1;
5502     },
5503     
5504     /**
5505      * Returns the index for a specified column dataIndex.
5506      * @param {String} dataIndex The column dataIndex
5507      * @return {Number} the index, or -1 if not found
5508      */
5509     
5510     findColumnIndex : function(dataIndex){
5511         for(var i = 0, len = this.config.length; i < len; i++){
5512             if(this.config[i].dataIndex == dataIndex){
5513                 return i;
5514             }
5515         }
5516         return -1;
5517     },
5518     
5519     
5520     moveColumn : function(oldIndex, newIndex){
5521         var c = this.config[oldIndex];
5522         this.config.splice(oldIndex, 1);
5523         this.config.splice(newIndex, 0, c);
5524         this.dataMap = null;
5525         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5526     },
5527
5528     isLocked : function(colIndex){
5529         return this.config[colIndex].locked === true;
5530     },
5531
5532     setLocked : function(colIndex, value, suppressEvent){
5533         if(this.isLocked(colIndex) == value){
5534             return;
5535         }
5536         this.config[colIndex].locked = value;
5537         if(!suppressEvent){
5538             this.fireEvent("columnlockchange", this, colIndex, value);
5539         }
5540     },
5541
5542     getTotalLockedWidth : function(){
5543         var totalWidth = 0;
5544         for(var i = 0; i < this.config.length; i++){
5545             if(this.isLocked(i) && !this.isHidden(i)){
5546                 this.totalWidth += this.getColumnWidth(i);
5547             }
5548         }
5549         return totalWidth;
5550     },
5551
5552     getLockedCount : function(){
5553         for(var i = 0, len = this.config.length; i < len; i++){
5554             if(!this.isLocked(i)){
5555                 return i;
5556             }
5557         }
5558         
5559         return this.config.length;
5560     },
5561
5562     /**
5563      * Returns the number of columns.
5564      * @return {Number}
5565      */
5566     getColumnCount : function(visibleOnly){
5567         if(visibleOnly === true){
5568             var c = 0;
5569             for(var i = 0, len = this.config.length; i < len; i++){
5570                 if(!this.isHidden(i)){
5571                     c++;
5572                 }
5573             }
5574             return c;
5575         }
5576         return this.config.length;
5577     },
5578
5579     /**
5580      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5581      * @param {Function} fn
5582      * @param {Object} scope (optional)
5583      * @return {Array} result
5584      */
5585     getColumnsBy : function(fn, scope){
5586         var r = [];
5587         for(var i = 0, len = this.config.length; i < len; i++){
5588             var c = this.config[i];
5589             if(fn.call(scope||this, c, i) === true){
5590                 r[r.length] = c;
5591             }
5592         }
5593         return r;
5594     },
5595
5596     /**
5597      * Returns true if the specified column is sortable.
5598      * @param {Number} col The column index
5599      * @return {Boolean}
5600      */
5601     isSortable : function(col){
5602         if(typeof this.config[col].sortable == "undefined"){
5603             return this.defaultSortable;
5604         }
5605         return this.config[col].sortable;
5606     },
5607
5608     /**
5609      * Returns the rendering (formatting) function defined for the column.
5610      * @param {Number} col The column index.
5611      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5612      */
5613     getRenderer : function(col){
5614         if(!this.config[col].renderer){
5615             return Roo.grid.ColumnModel.defaultRenderer;
5616         }
5617         return this.config[col].renderer;
5618     },
5619
5620     /**
5621      * Sets the rendering (formatting) function for a column.
5622      * @param {Number} col The column index
5623      * @param {Function} fn The function to use to process the cell's raw data
5624      * to return HTML markup for the grid view. The render function is called with
5625      * the following parameters:<ul>
5626      * <li>Data value.</li>
5627      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5628      * <li>css A CSS style string to apply to the table cell.</li>
5629      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5630      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5631      * <li>Row index</li>
5632      * <li>Column index</li>
5633      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5634      */
5635     setRenderer : function(col, fn){
5636         this.config[col].renderer = fn;
5637     },
5638
5639     /**
5640      * Returns the width for the specified column.
5641      * @param {Number} col The column index
5642      * @return {Number}
5643      */
5644     getColumnWidth : function(col){
5645         return this.config[col].width * 1 || this.defaultWidth;
5646     },
5647
5648     /**
5649      * Sets the width for a column.
5650      * @param {Number} col The column index
5651      * @param {Number} width The new width
5652      */
5653     setColumnWidth : function(col, width, suppressEvent){
5654         this.config[col].width = width;
5655         this.totalWidth = null;
5656         if(!suppressEvent){
5657              this.fireEvent("widthchange", this, col, width);
5658         }
5659     },
5660
5661     /**
5662      * Returns the total width of all columns.
5663      * @param {Boolean} includeHidden True to include hidden column widths
5664      * @return {Number}
5665      */
5666     getTotalWidth : function(includeHidden){
5667         if(!this.totalWidth){
5668             this.totalWidth = 0;
5669             for(var i = 0, len = this.config.length; i < len; i++){
5670                 if(includeHidden || !this.isHidden(i)){
5671                     this.totalWidth += this.getColumnWidth(i);
5672                 }
5673             }
5674         }
5675         return this.totalWidth;
5676     },
5677
5678     /**
5679      * Returns the header for the specified column.
5680      * @param {Number} col The column index
5681      * @return {String}
5682      */
5683     getColumnHeader : function(col){
5684         return this.config[col].header;
5685     },
5686
5687     /**
5688      * Sets the header for a column.
5689      * @param {Number} col The column index
5690      * @param {String} header The new header
5691      */
5692     setColumnHeader : function(col, header){
5693         this.config[col].header = header;
5694         this.fireEvent("headerchange", this, col, header);
5695     },
5696
5697     /**
5698      * Returns the tooltip for the specified column.
5699      * @param {Number} col The column index
5700      * @return {String}
5701      */
5702     getColumnTooltip : function(col){
5703             return this.config[col].tooltip;
5704     },
5705     /**
5706      * Sets the tooltip for a column.
5707      * @param {Number} col The column index
5708      * @param {String} tooltip The new tooltip
5709      */
5710     setColumnTooltip : function(col, tooltip){
5711             this.config[col].tooltip = tooltip;
5712     },
5713
5714     /**
5715      * Returns the dataIndex for the specified column.
5716      * @param {Number} col The column index
5717      * @return {Number}
5718      */
5719     getDataIndex : function(col){
5720         return this.config[col].dataIndex;
5721     },
5722
5723     /**
5724      * Sets the dataIndex for a column.
5725      * @param {Number} col The column index
5726      * @param {Number} dataIndex The new dataIndex
5727      */
5728     setDataIndex : function(col, dataIndex){
5729         this.config[col].dataIndex = dataIndex;
5730     },
5731
5732     
5733     
5734     /**
5735      * Returns true if the cell is editable.
5736      * @param {Number} colIndex The column index
5737      * @param {Number} rowIndex The row index - this is nto actually used..?
5738      * @return {Boolean}
5739      */
5740     isCellEditable : function(colIndex, rowIndex){
5741         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5742     },
5743
5744     /**
5745      * Returns the editor defined for the cell/column.
5746      * return false or null to disable editing.
5747      * @param {Number} colIndex The column index
5748      * @param {Number} rowIndex The row index
5749      * @return {Object}
5750      */
5751     getCellEditor : function(colIndex, rowIndex){
5752         return this.config[colIndex].editor;
5753     },
5754
5755     /**
5756      * Sets if a column is editable.
5757      * @param {Number} col The column index
5758      * @param {Boolean} editable True if the column is editable
5759      */
5760     setEditable : function(col, editable){
5761         this.config[col].editable = editable;
5762     },
5763
5764
5765     /**
5766      * Returns true if the column is hidden.
5767      * @param {Number} colIndex The column index
5768      * @return {Boolean}
5769      */
5770     isHidden : function(colIndex){
5771         return this.config[colIndex].hidden;
5772     },
5773
5774
5775     /**
5776      * Returns true if the column width cannot be changed
5777      */
5778     isFixed : function(colIndex){
5779         return this.config[colIndex].fixed;
5780     },
5781
5782     /**
5783      * Returns true if the column can be resized
5784      * @return {Boolean}
5785      */
5786     isResizable : function(colIndex){
5787         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5788     },
5789     /**
5790      * Sets if a column is hidden.
5791      * @param {Number} colIndex The column index
5792      * @param {Boolean} hidden True if the column is hidden
5793      */
5794     setHidden : function(colIndex, hidden){
5795         this.config[colIndex].hidden = hidden;
5796         this.totalWidth = null;
5797         this.fireEvent("hiddenchange", this, colIndex, hidden);
5798     },
5799
5800     /**
5801      * Sets the editor for a column.
5802      * @param {Number} col The column index
5803      * @param {Object} editor The editor object
5804      */
5805     setEditor : function(col, editor){
5806         this.config[col].editor = editor;
5807     }
5808 });
5809
5810 Roo.grid.ColumnModel.defaultRenderer = function(value)
5811 {
5812     if(typeof value == "object") {
5813         return value;
5814     }
5815         if(typeof value == "string" && value.length < 1){
5816             return "&#160;";
5817         }
5818     
5819         return String.format("{0}", value);
5820 };
5821
5822 // Alias for backwards compatibility
5823 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5824 /*
5825  * Based on:
5826  * Ext JS Library 1.1.1
5827  * Copyright(c) 2006-2007, Ext JS, LLC.
5828  *
5829  * Originally Released Under LGPL - original licence link has changed is not relivant.
5830  *
5831  * Fork - LGPL
5832  * <script type="text/javascript">
5833  */
5834  
5835 /**
5836  * @class Roo.LoadMask
5837  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5838  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5839  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5840  * element's UpdateManager load indicator and will be destroyed after the initial load.
5841  * @constructor
5842  * Create a new LoadMask
5843  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5844  * @param {Object} config The config object
5845  */
5846 Roo.LoadMask = function(el, config){
5847     this.el = Roo.get(el);
5848     Roo.apply(this, config);
5849     if(this.store){
5850         this.store.on('beforeload', this.onBeforeLoad, this);
5851         this.store.on('load', this.onLoad, this);
5852         this.store.on('loadexception', this.onLoadException, this);
5853         this.removeMask = false;
5854     }else{
5855         var um = this.el.getUpdateManager();
5856         um.showLoadIndicator = false; // disable the default indicator
5857         um.on('beforeupdate', this.onBeforeLoad, this);
5858         um.on('update', this.onLoad, this);
5859         um.on('failure', this.onLoad, this);
5860         this.removeMask = true;
5861     }
5862 };
5863
5864 Roo.LoadMask.prototype = {
5865     /**
5866      * @cfg {Boolean} removeMask
5867      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5868      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5869      */
5870     /**
5871      * @cfg {String} msg
5872      * The text to display in a centered loading message box (defaults to 'Loading...')
5873      */
5874     msg : 'Loading...',
5875     /**
5876      * @cfg {String} msgCls
5877      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5878      */
5879     msgCls : 'x-mask-loading',
5880
5881     /**
5882      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5883      * @type Boolean
5884      */
5885     disabled: false,
5886
5887     /**
5888      * Disables the mask to prevent it from being displayed
5889      */
5890     disable : function(){
5891        this.disabled = true;
5892     },
5893
5894     /**
5895      * Enables the mask so that it can be displayed
5896      */
5897     enable : function(){
5898         this.disabled = false;
5899     },
5900     
5901     onLoadException : function()
5902     {
5903         Roo.log(arguments);
5904         
5905         if (typeof(arguments[3]) != 'undefined') {
5906             Roo.MessageBox.alert("Error loading",arguments[3]);
5907         } 
5908         /*
5909         try {
5910             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5911                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5912             }   
5913         } catch(e) {
5914             
5915         }
5916         */
5917     
5918         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5919     },
5920     // private
5921     onLoad : function()
5922     {
5923         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5924     },
5925
5926     // private
5927     onBeforeLoad : function(){
5928         if(!this.disabled){
5929             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5930         }
5931     },
5932
5933     // private
5934     destroy : function(){
5935         if(this.store){
5936             this.store.un('beforeload', this.onBeforeLoad, this);
5937             this.store.un('load', this.onLoad, this);
5938             this.store.un('loadexception', this.onLoadException, this);
5939         }else{
5940             var um = this.el.getUpdateManager();
5941             um.un('beforeupdate', this.onBeforeLoad, this);
5942             um.un('update', this.onLoad, this);
5943             um.un('failure', this.onLoad, this);
5944         }
5945     }
5946 };/*
5947  * - LGPL
5948  *
5949  * table
5950  * 
5951  */
5952
5953 /**
5954  * @class Roo.bootstrap.Table
5955  * @extends Roo.bootstrap.Component
5956  * Bootstrap Table class
5957  * @cfg {String} cls table class
5958  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5959  * @cfg {String} bgcolor Specifies the background color for a table
5960  * @cfg {Number} border Specifies whether the table cells should have borders or not
5961  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5962  * @cfg {Number} cellspacing Specifies the space between cells
5963  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5964  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5965  * @cfg {String} sortable Specifies that the table should be sortable
5966  * @cfg {String} summary Specifies a summary of the content of a table
5967  * @cfg {Number} width Specifies the width of a table
5968  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5969  * 
5970  * @cfg {boolean} striped Should the rows be alternative striped
5971  * @cfg {boolean} bordered Add borders to the table
5972  * @cfg {boolean} hover Add hover highlighting
5973  * @cfg {boolean} condensed Format condensed
5974  * @cfg {boolean} responsive Format condensed
5975  * @cfg {Boolean} loadMask (true|false) default false
5976  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5977  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5978  * @cfg {Boolean} rowSelection (true|false) default false
5979  * @cfg {Boolean} cellSelection (true|false) default false
5980  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5981  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5982  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5983  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5984  
5985  * 
5986  * @constructor
5987  * Create a new Table
5988  * @param {Object} config The config object
5989  */
5990
5991 Roo.bootstrap.Table = function(config){
5992     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5993     
5994   
5995     
5996     // BC...
5997     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5998     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5999     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6000     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6001     
6002     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6003     if (this.sm) {
6004         this.sm.grid = this;
6005         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6006         this.sm = this.selModel;
6007         this.sm.xmodule = this.xmodule || false;
6008     }
6009     
6010     if (this.cm && typeof(this.cm.config) == 'undefined') {
6011         this.colModel = new Roo.grid.ColumnModel(this.cm);
6012         this.cm = this.colModel;
6013         this.cm.xmodule = this.xmodule || false;
6014     }
6015     if (this.store) {
6016         this.store= Roo.factory(this.store, Roo.data);
6017         this.ds = this.store;
6018         this.ds.xmodule = this.xmodule || false;
6019          
6020     }
6021     if (this.footer && this.store) {
6022         this.footer.dataSource = this.ds;
6023         this.footer = Roo.factory(this.footer);
6024     }
6025     
6026     /** @private */
6027     this.addEvents({
6028         /**
6029          * @event cellclick
6030          * Fires when a cell is clicked
6031          * @param {Roo.bootstrap.Table} this
6032          * @param {Roo.Element} el
6033          * @param {Number} rowIndex
6034          * @param {Number} columnIndex
6035          * @param {Roo.EventObject} e
6036          */
6037         "cellclick" : true,
6038         /**
6039          * @event celldblclick
6040          * Fires when a cell is double clicked
6041          * @param {Roo.bootstrap.Table} this
6042          * @param {Roo.Element} el
6043          * @param {Number} rowIndex
6044          * @param {Number} columnIndex
6045          * @param {Roo.EventObject} e
6046          */
6047         "celldblclick" : true,
6048         /**
6049          * @event rowclick
6050          * Fires when a row is clicked
6051          * @param {Roo.bootstrap.Table} this
6052          * @param {Roo.Element} el
6053          * @param {Number} rowIndex
6054          * @param {Roo.EventObject} e
6055          */
6056         "rowclick" : true,
6057         /**
6058          * @event rowdblclick
6059          * Fires when a row is double clicked
6060          * @param {Roo.bootstrap.Table} this
6061          * @param {Roo.Element} el
6062          * @param {Number} rowIndex
6063          * @param {Roo.EventObject} e
6064          */
6065         "rowdblclick" : true,
6066         /**
6067          * @event mouseover
6068          * Fires when a mouseover occur
6069          * @param {Roo.bootstrap.Table} this
6070          * @param {Roo.Element} el
6071          * @param {Number} rowIndex
6072          * @param {Number} columnIndex
6073          * @param {Roo.EventObject} e
6074          */
6075         "mouseover" : true,
6076         /**
6077          * @event mouseout
6078          * Fires when a mouseout occur
6079          * @param {Roo.bootstrap.Table} this
6080          * @param {Roo.Element} el
6081          * @param {Number} rowIndex
6082          * @param {Number} columnIndex
6083          * @param {Roo.EventObject} e
6084          */
6085         "mouseout" : true,
6086         /**
6087          * @event rowclass
6088          * Fires when a row is rendered, so you can change add a style to it.
6089          * @param {Roo.bootstrap.Table} this
6090          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6091          */
6092         'rowclass' : true,
6093           /**
6094          * @event rowsrendered
6095          * Fires when all the  rows have been rendered
6096          * @param {Roo.bootstrap.Table} this
6097          */
6098         'rowsrendered' : true,
6099         /**
6100          * @event contextmenu
6101          * The raw contextmenu event for the entire grid.
6102          * @param {Roo.EventObject} e
6103          */
6104         "contextmenu" : true,
6105         /**
6106          * @event rowcontextmenu
6107          * Fires when a row is right clicked
6108          * @param {Roo.bootstrap.Table} this
6109          * @param {Number} rowIndex
6110          * @param {Roo.EventObject} e
6111          */
6112         "rowcontextmenu" : true,
6113         /**
6114          * @event cellcontextmenu
6115          * Fires when a cell is right clicked
6116          * @param {Roo.bootstrap.Table} this
6117          * @param {Number} rowIndex
6118          * @param {Number} cellIndex
6119          * @param {Roo.EventObject} e
6120          */
6121          "cellcontextmenu" : true,
6122          /**
6123          * @event headercontextmenu
6124          * Fires when a header is right clicked
6125          * @param {Roo.bootstrap.Table} this
6126          * @param {Number} columnIndex
6127          * @param {Roo.EventObject} e
6128          */
6129         "headercontextmenu" : true
6130     });
6131 };
6132
6133 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6134     
6135     cls: false,
6136     align: false,
6137     bgcolor: false,
6138     border: false,
6139     cellpadding: false,
6140     cellspacing: false,
6141     frame: false,
6142     rules: false,
6143     sortable: false,
6144     summary: false,
6145     width: false,
6146     striped : false,
6147     scrollBody : false,
6148     bordered: false,
6149     hover:  false,
6150     condensed : false,
6151     responsive : false,
6152     sm : false,
6153     cm : false,
6154     store : false,
6155     loadMask : false,
6156     footerShow : true,
6157     headerShow : true,
6158   
6159     rowSelection : false,
6160     cellSelection : false,
6161     layout : false,
6162     
6163     // Roo.Element - the tbody
6164     mainBody: false,
6165     // Roo.Element - thead element
6166     mainHead: false,
6167     
6168     container: false, // used by gridpanel...
6169     
6170     lazyLoad : false,
6171     
6172     CSS : Roo.util.CSS,
6173     
6174     auto_hide_footer : false,
6175     
6176     getAutoCreate : function()
6177     {
6178         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6179         
6180         cfg = {
6181             tag: 'table',
6182             cls : 'table',
6183             cn : []
6184         };
6185         if (this.scrollBody) {
6186             cfg.cls += ' table-body-fixed';
6187         }    
6188         if (this.striped) {
6189             cfg.cls += ' table-striped';
6190         }
6191         
6192         if (this.hover) {
6193             cfg.cls += ' table-hover';
6194         }
6195         if (this.bordered) {
6196             cfg.cls += ' table-bordered';
6197         }
6198         if (this.condensed) {
6199             cfg.cls += ' table-condensed';
6200         }
6201         if (this.responsive) {
6202             cfg.cls += ' table-responsive';
6203         }
6204         
6205         if (this.cls) {
6206             cfg.cls+=  ' ' +this.cls;
6207         }
6208         
6209         // this lot should be simplifed...
6210         var _t = this;
6211         var cp = [
6212             'align',
6213             'bgcolor',
6214             'border',
6215             'cellpadding',
6216             'cellspacing',
6217             'frame',
6218             'rules',
6219             'sortable',
6220             'summary',
6221             'width'
6222         ].forEach(function(k) {
6223             if (_t[k]) {
6224                 cfg[k] = _t[k];
6225             }
6226         });
6227         
6228         
6229         if (this.layout) {
6230             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6231         }
6232         
6233         if(this.store || this.cm){
6234             if(this.headerShow){
6235                 cfg.cn.push(this.renderHeader());
6236             }
6237             
6238             cfg.cn.push(this.renderBody());
6239             
6240             if(this.footerShow){
6241                 cfg.cn.push(this.renderFooter());
6242             }
6243             // where does this come from?
6244             //cfg.cls+=  ' TableGrid';
6245         }
6246         
6247         return { cn : [ cfg ] };
6248     },
6249     
6250     initEvents : function()
6251     {   
6252         if(!this.store || !this.cm){
6253             return;
6254         }
6255         if (this.selModel) {
6256             this.selModel.initEvents();
6257         }
6258         
6259         
6260         //Roo.log('initEvents with ds!!!!');
6261         
6262         this.mainBody = this.el.select('tbody', true).first();
6263         this.mainHead = this.el.select('thead', true).first();
6264         this.mainFoot = this.el.select('tfoot', true).first();
6265         
6266         
6267         
6268         var _this = this;
6269         
6270         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6271             e.on('click', _this.sort, _this);
6272         });
6273         
6274         this.mainBody.on("click", this.onClick, this);
6275         this.mainBody.on("dblclick", this.onDblClick, this);
6276         
6277         // why is this done????? = it breaks dialogs??
6278         //this.parent().el.setStyle('position', 'relative');
6279         
6280         
6281         if (this.footer) {
6282             this.footer.parentId = this.id;
6283             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6284             
6285             if(this.lazyLoad){
6286                 this.el.select('tfoot tr td').first().addClass('hide');
6287             }
6288         } 
6289         
6290         if(this.loadMask) {
6291             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6292         }
6293         
6294         this.store.on('load', this.onLoad, this);
6295         this.store.on('beforeload', this.onBeforeLoad, this);
6296         this.store.on('update', this.onUpdate, this);
6297         this.store.on('add', this.onAdd, this);
6298         this.store.on("clear", this.clear, this);
6299         
6300         this.el.on("contextmenu", this.onContextMenu, this);
6301         
6302         this.mainBody.on('scroll', this.onBodyScroll, this);
6303         
6304         this.cm.on("headerchange", this.onHeaderChange, this);
6305         
6306         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6307         
6308     },
6309     
6310     onContextMenu : function(e, t)
6311     {
6312         this.processEvent("contextmenu", e);
6313     },
6314     
6315     processEvent : function(name, e)
6316     {
6317         if (name != 'touchstart' ) {
6318             this.fireEvent(name, e);    
6319         }
6320         
6321         var t = e.getTarget();
6322         
6323         var cell = Roo.get(t);
6324         
6325         if(!cell){
6326             return;
6327         }
6328         
6329         if(cell.findParent('tfoot', false, true)){
6330             return;
6331         }
6332         
6333         if(cell.findParent('thead', false, true)){
6334             
6335             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6336                 cell = Roo.get(t).findParent('th', false, true);
6337                 if (!cell) {
6338                     Roo.log("failed to find th in thead?");
6339                     Roo.log(e.getTarget());
6340                     return;
6341                 }
6342             }
6343             
6344             var cellIndex = cell.dom.cellIndex;
6345             
6346             var ename = name == 'touchstart' ? 'click' : name;
6347             this.fireEvent("header" + ename, this, cellIndex, e);
6348             
6349             return;
6350         }
6351         
6352         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6353             cell = Roo.get(t).findParent('td', false, true);
6354             if (!cell) {
6355                 Roo.log("failed to find th in tbody?");
6356                 Roo.log(e.getTarget());
6357                 return;
6358             }
6359         }
6360         
6361         var row = cell.findParent('tr', false, true);
6362         var cellIndex = cell.dom.cellIndex;
6363         var rowIndex = row.dom.rowIndex - 1;
6364         
6365         if(row !== false){
6366             
6367             this.fireEvent("row" + name, this, rowIndex, e);
6368             
6369             if(cell !== false){
6370             
6371                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6372             }
6373         }
6374         
6375     },
6376     
6377     onMouseover : function(e, el)
6378     {
6379         var cell = Roo.get(el);
6380         
6381         if(!cell){
6382             return;
6383         }
6384         
6385         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6386             cell = cell.findParent('td', false, true);
6387         }
6388         
6389         var row = cell.findParent('tr', false, true);
6390         var cellIndex = cell.dom.cellIndex;
6391         var rowIndex = row.dom.rowIndex - 1; // start from 0
6392         
6393         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6394         
6395     },
6396     
6397     onMouseout : function(e, el)
6398     {
6399         var cell = Roo.get(el);
6400         
6401         if(!cell){
6402             return;
6403         }
6404         
6405         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6406             cell = cell.findParent('td', false, true);
6407         }
6408         
6409         var row = cell.findParent('tr', false, true);
6410         var cellIndex = cell.dom.cellIndex;
6411         var rowIndex = row.dom.rowIndex - 1; // start from 0
6412         
6413         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6414         
6415     },
6416     
6417     onClick : function(e, el)
6418     {
6419         var cell = Roo.get(el);
6420         
6421         if(!cell || (!this.cellSelection && !this.rowSelection)){
6422             return;
6423         }
6424         
6425         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6426             cell = cell.findParent('td', false, true);
6427         }
6428         
6429         if(!cell || typeof(cell) == 'undefined'){
6430             return;
6431         }
6432         
6433         var row = cell.findParent('tr', false, true);
6434         
6435         if(!row || typeof(row) == 'undefined'){
6436             return;
6437         }
6438         
6439         var cellIndex = cell.dom.cellIndex;
6440         var rowIndex = this.getRowIndex(row);
6441         
6442         // why??? - should these not be based on SelectionModel?
6443         if(this.cellSelection){
6444             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6445         }
6446         
6447         if(this.rowSelection){
6448             this.fireEvent('rowclick', this, row, rowIndex, e);
6449         }
6450         
6451         
6452     },
6453         
6454     onDblClick : function(e,el)
6455     {
6456         var cell = Roo.get(el);
6457         
6458         if(!cell || (!this.cellSelection && !this.rowSelection)){
6459             return;
6460         }
6461         
6462         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6463             cell = cell.findParent('td', false, true);
6464         }
6465         
6466         if(!cell || typeof(cell) == 'undefined'){
6467             return;
6468         }
6469         
6470         var row = cell.findParent('tr', false, true);
6471         
6472         if(!row || typeof(row) == 'undefined'){
6473             return;
6474         }
6475         
6476         var cellIndex = cell.dom.cellIndex;
6477         var rowIndex = this.getRowIndex(row);
6478         
6479         if(this.cellSelection){
6480             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6481         }
6482         
6483         if(this.rowSelection){
6484             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6485         }
6486     },
6487     
6488     sort : function(e,el)
6489     {
6490         var col = Roo.get(el);
6491         
6492         if(!col.hasClass('sortable')){
6493             return;
6494         }
6495         
6496         var sort = col.attr('sort');
6497         var dir = 'ASC';
6498         
6499         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6500             dir = 'DESC';
6501         }
6502         
6503         this.store.sortInfo = {field : sort, direction : dir};
6504         
6505         if (this.footer) {
6506             Roo.log("calling footer first");
6507             this.footer.onClick('first');
6508         } else {
6509         
6510             this.store.load({ params : { start : 0 } });
6511         }
6512     },
6513     
6514     renderHeader : function()
6515     {
6516         var header = {
6517             tag: 'thead',
6518             cn : []
6519         };
6520         
6521         var cm = this.cm;
6522         this.totalWidth = 0;
6523         
6524         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6525             
6526             var config = cm.config[i];
6527             
6528             var c = {
6529                 tag: 'th',
6530                 cls : 'x-hcol-' + i,
6531                 style : '',
6532                 html: cm.getColumnHeader(i)
6533             };
6534             
6535             var hh = '';
6536             
6537             if(typeof(config.sortable) != 'undefined' && config.sortable){
6538                 c.cls = 'sortable';
6539                 c.html = '<i class="glyphicon"></i>' + c.html;
6540             }
6541             
6542             if(typeof(config.lgHeader) != 'undefined'){
6543                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6544             }
6545             
6546             if(typeof(config.mdHeader) != 'undefined'){
6547                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6548             }
6549             
6550             if(typeof(config.smHeader) != 'undefined'){
6551                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6552             }
6553             
6554             if(typeof(config.xsHeader) != 'undefined'){
6555                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6556             }
6557             
6558             if(hh.length){
6559                 c.html = hh;
6560             }
6561             
6562             if(typeof(config.tooltip) != 'undefined'){
6563                 c.tooltip = config.tooltip;
6564             }
6565             
6566             if(typeof(config.colspan) != 'undefined'){
6567                 c.colspan = config.colspan;
6568             }
6569             
6570             if(typeof(config.hidden) != 'undefined' && config.hidden){
6571                 c.style += ' display:none;';
6572             }
6573             
6574             if(typeof(config.dataIndex) != 'undefined'){
6575                 c.sort = config.dataIndex;
6576             }
6577             
6578            
6579             
6580             if(typeof(config.align) != 'undefined' && config.align.length){
6581                 c.style += ' text-align:' + config.align + ';';
6582             }
6583             
6584             if(typeof(config.width) != 'undefined'){
6585                 c.style += ' width:' + config.width + 'px;';
6586                 this.totalWidth += config.width;
6587             } else {
6588                 this.totalWidth += 100; // assume minimum of 100 per column?
6589             }
6590             
6591             if(typeof(config.cls) != 'undefined'){
6592                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6593             }
6594             
6595             ['xs','sm','md','lg'].map(function(size){
6596                 
6597                 if(typeof(config[size]) == 'undefined'){
6598                     return;
6599                 }
6600                 
6601                 if (!config[size]) { // 0 = hidden
6602                     c.cls += ' hidden-' + size;
6603                     return;
6604                 }
6605                 
6606                 c.cls += ' col-' + size + '-' + config[size];
6607
6608             });
6609             
6610             header.cn.push(c)
6611         }
6612         
6613         return header;
6614     },
6615     
6616     renderBody : function()
6617     {
6618         var body = {
6619             tag: 'tbody',
6620             cn : [
6621                 {
6622                     tag: 'tr',
6623                     cn : [
6624                         {
6625                             tag : 'td',
6626                             colspan :  this.cm.getColumnCount()
6627                         }
6628                     ]
6629                 }
6630             ]
6631         };
6632         
6633         return body;
6634     },
6635     
6636     renderFooter : function()
6637     {
6638         var footer = {
6639             tag: 'tfoot',
6640             cn : [
6641                 {
6642                     tag: 'tr',
6643                     cn : [
6644                         {
6645                             tag : 'td',
6646                             colspan :  this.cm.getColumnCount()
6647                         }
6648                     ]
6649                 }
6650             ]
6651         };
6652         
6653         return footer;
6654     },
6655     
6656     
6657     
6658     onLoad : function()
6659     {
6660 //        Roo.log('ds onload');
6661         this.clear();
6662         
6663         var _this = this;
6664         var cm = this.cm;
6665         var ds = this.store;
6666         
6667         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6668             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6669             if (_this.store.sortInfo) {
6670                     
6671                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6672                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6673                 }
6674                 
6675                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6676                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6677                 }
6678             }
6679         });
6680         
6681         var tbody =  this.mainBody;
6682               
6683         if(ds.getCount() > 0){
6684             ds.data.each(function(d,rowIndex){
6685                 var row =  this.renderRow(cm, ds, rowIndex);
6686                 
6687                 tbody.createChild(row);
6688                 
6689                 var _this = this;
6690                 
6691                 if(row.cellObjects.length){
6692                     Roo.each(row.cellObjects, function(r){
6693                         _this.renderCellObject(r);
6694                     })
6695                 }
6696                 
6697             }, this);
6698         }
6699         
6700         var tfoot = this.el.select('tfoot', true).first();
6701         
6702         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6703             
6704             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6705             
6706             var total = this.ds.getTotalCount();
6707             
6708             if(this.footer.pageSize < total){
6709                 this.mainFoot.show();
6710             }
6711         }
6712         
6713         Roo.each(this.el.select('tbody td', true).elements, function(e){
6714             e.on('mouseover', _this.onMouseover, _this);
6715         });
6716         
6717         Roo.each(this.el.select('tbody td', true).elements, function(e){
6718             e.on('mouseout', _this.onMouseout, _this);
6719         });
6720         this.fireEvent('rowsrendered', this);
6721         
6722         this.autoSize();
6723     },
6724     
6725     
6726     onUpdate : function(ds,record)
6727     {
6728         this.refreshRow(record);
6729         this.autoSize();
6730     },
6731     
6732     onRemove : function(ds, record, index, isUpdate){
6733         if(isUpdate !== true){
6734             this.fireEvent("beforerowremoved", this, index, record);
6735         }
6736         var bt = this.mainBody.dom;
6737         
6738         var rows = this.el.select('tbody > tr', true).elements;
6739         
6740         if(typeof(rows[index]) != 'undefined'){
6741             bt.removeChild(rows[index].dom);
6742         }
6743         
6744 //        if(bt.rows[index]){
6745 //            bt.removeChild(bt.rows[index]);
6746 //        }
6747         
6748         if(isUpdate !== true){
6749             //this.stripeRows(index);
6750             //this.syncRowHeights(index, index);
6751             //this.layout();
6752             this.fireEvent("rowremoved", this, index, record);
6753         }
6754     },
6755     
6756     onAdd : function(ds, records, rowIndex)
6757     {
6758         //Roo.log('on Add called');
6759         // - note this does not handle multiple adding very well..
6760         var bt = this.mainBody.dom;
6761         for (var i =0 ; i < records.length;i++) {
6762             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6763             //Roo.log(records[i]);
6764             //Roo.log(this.store.getAt(rowIndex+i));
6765             this.insertRow(this.store, rowIndex + i, false);
6766             return;
6767         }
6768         
6769     },
6770     
6771     
6772     refreshRow : function(record){
6773         var ds = this.store, index;
6774         if(typeof record == 'number'){
6775             index = record;
6776             record = ds.getAt(index);
6777         }else{
6778             index = ds.indexOf(record);
6779         }
6780         this.insertRow(ds, index, true);
6781         this.autoSize();
6782         this.onRemove(ds, record, index+1, true);
6783         this.autoSize();
6784         //this.syncRowHeights(index, index);
6785         //this.layout();
6786         this.fireEvent("rowupdated", this, index, record);
6787     },
6788     
6789     insertRow : function(dm, rowIndex, isUpdate){
6790         
6791         if(!isUpdate){
6792             this.fireEvent("beforerowsinserted", this, rowIndex);
6793         }
6794             //var s = this.getScrollState();
6795         var row = this.renderRow(this.cm, this.store, rowIndex);
6796         // insert before rowIndex..
6797         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6798         
6799         var _this = this;
6800                 
6801         if(row.cellObjects.length){
6802             Roo.each(row.cellObjects, function(r){
6803                 _this.renderCellObject(r);
6804             })
6805         }
6806             
6807         if(!isUpdate){
6808             this.fireEvent("rowsinserted", this, rowIndex);
6809             //this.syncRowHeights(firstRow, lastRow);
6810             //this.stripeRows(firstRow);
6811             //this.layout();
6812         }
6813         
6814     },
6815     
6816     
6817     getRowDom : function(rowIndex)
6818     {
6819         var rows = this.el.select('tbody > tr', true).elements;
6820         
6821         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6822         
6823     },
6824     // returns the object tree for a tr..
6825   
6826     
6827     renderRow : function(cm, ds, rowIndex) 
6828     {
6829         var d = ds.getAt(rowIndex);
6830         
6831         var row = {
6832             tag : 'tr',
6833             cls : 'x-row-' + rowIndex,
6834             cn : []
6835         };
6836             
6837         var cellObjects = [];
6838         
6839         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6840             var config = cm.config[i];
6841             
6842             var renderer = cm.getRenderer(i);
6843             var value = '';
6844             var id = false;
6845             
6846             if(typeof(renderer) !== 'undefined'){
6847                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6848             }
6849             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6850             // and are rendered into the cells after the row is rendered - using the id for the element.
6851             
6852             if(typeof(value) === 'object'){
6853                 id = Roo.id();
6854                 cellObjects.push({
6855                     container : id,
6856                     cfg : value 
6857                 })
6858             }
6859             
6860             var rowcfg = {
6861                 record: d,
6862                 rowIndex : rowIndex,
6863                 colIndex : i,
6864                 rowClass : ''
6865             };
6866
6867             this.fireEvent('rowclass', this, rowcfg);
6868             
6869             var td = {
6870                 tag: 'td',
6871                 cls : rowcfg.rowClass + ' x-col-' + i,
6872                 style: '',
6873                 html: (typeof(value) === 'object') ? '' : value
6874             };
6875             
6876             if (id) {
6877                 td.id = id;
6878             }
6879             
6880             if(typeof(config.colspan) != 'undefined'){
6881                 td.colspan = config.colspan;
6882             }
6883             
6884             if(typeof(config.hidden) != 'undefined' && config.hidden){
6885                 td.style += ' display:none;';
6886             }
6887             
6888             if(typeof(config.align) != 'undefined' && config.align.length){
6889                 td.style += ' text-align:' + config.align + ';';
6890             }
6891             if(typeof(config.valign) != 'undefined' && config.valign.length){
6892                 td.style += ' vertical-align:' + config.valign + ';';
6893             }
6894             
6895             if(typeof(config.width) != 'undefined'){
6896                 td.style += ' width:' +  config.width + 'px;';
6897             }
6898             
6899             if(typeof(config.cursor) != 'undefined'){
6900                 td.style += ' cursor:' +  config.cursor + ';';
6901             }
6902             
6903             if(typeof(config.cls) != 'undefined'){
6904                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6905             }
6906             
6907             ['xs','sm','md','lg'].map(function(size){
6908                 
6909                 if(typeof(config[size]) == 'undefined'){
6910                     return;
6911                 }
6912                 
6913                 if (!config[size]) { // 0 = hidden
6914                     td.cls += ' hidden-' + size;
6915                     return;
6916                 }
6917                 
6918                 td.cls += ' col-' + size + '-' + config[size];
6919
6920             });
6921             
6922             row.cn.push(td);
6923            
6924         }
6925         
6926         row.cellObjects = cellObjects;
6927         
6928         return row;
6929           
6930     },
6931     
6932     
6933     
6934     onBeforeLoad : function()
6935     {
6936         
6937     },
6938      /**
6939      * Remove all rows
6940      */
6941     clear : function()
6942     {
6943         this.el.select('tbody', true).first().dom.innerHTML = '';
6944     },
6945     /**
6946      * Show or hide a row.
6947      * @param {Number} rowIndex to show or hide
6948      * @param {Boolean} state hide
6949      */
6950     setRowVisibility : function(rowIndex, state)
6951     {
6952         var bt = this.mainBody.dom;
6953         
6954         var rows = this.el.select('tbody > tr', true).elements;
6955         
6956         if(typeof(rows[rowIndex]) == 'undefined'){
6957             return;
6958         }
6959         rows[rowIndex].dom.style.display = state ? '' : 'none';
6960     },
6961     
6962     
6963     getSelectionModel : function(){
6964         if(!this.selModel){
6965             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6966         }
6967         return this.selModel;
6968     },
6969     /*
6970      * Render the Roo.bootstrap object from renderder
6971      */
6972     renderCellObject : function(r)
6973     {
6974         var _this = this;
6975         
6976         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6977         
6978         var t = r.cfg.render(r.container);
6979         
6980         if(r.cfg.cn){
6981             Roo.each(r.cfg.cn, function(c){
6982                 var child = {
6983                     container: t.getChildContainer(),
6984                     cfg: c
6985                 };
6986                 _this.renderCellObject(child);
6987             })
6988         }
6989     },
6990     
6991     getRowIndex : function(row)
6992     {
6993         var rowIndex = -1;
6994         
6995         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6996             if(el != row){
6997                 return;
6998             }
6999             
7000             rowIndex = index;
7001         });
7002         
7003         return rowIndex;
7004     },
7005      /**
7006      * Returns the grid's underlying element = used by panel.Grid
7007      * @return {Element} The element
7008      */
7009     getGridEl : function(){
7010         return this.el;
7011     },
7012      /**
7013      * Forces a resize - used by panel.Grid
7014      * @return {Element} The element
7015      */
7016     autoSize : function()
7017     {
7018         //var ctr = Roo.get(this.container.dom.parentElement);
7019         var ctr = Roo.get(this.el.dom);
7020         
7021         var thd = this.getGridEl().select('thead',true).first();
7022         var tbd = this.getGridEl().select('tbody', true).first();
7023         var tfd = this.getGridEl().select('tfoot', true).first();
7024         
7025         var cw = ctr.getWidth();
7026         
7027         if (tbd) {
7028             
7029             tbd.setSize(ctr.getWidth(),
7030                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7031             );
7032             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7033             cw -= barsize;
7034         }
7035         cw = Math.max(cw, this.totalWidth);
7036         this.getGridEl().select('tr',true).setWidth(cw);
7037         // resize 'expandable coloumn?
7038         
7039         return; // we doe not have a view in this design..
7040         
7041     },
7042     onBodyScroll: function()
7043     {
7044         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7045         if(this.mainHead){
7046             this.mainHead.setStyle({
7047                 'position' : 'relative',
7048                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7049             });
7050         }
7051         
7052         if(this.lazyLoad){
7053             
7054             var scrollHeight = this.mainBody.dom.scrollHeight;
7055             
7056             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7057             
7058             var height = this.mainBody.getHeight();
7059             
7060             if(scrollHeight - height == scrollTop) {
7061                 
7062                 var total = this.ds.getTotalCount();
7063                 
7064                 if(this.footer.cursor + this.footer.pageSize < total){
7065                     
7066                     this.footer.ds.load({
7067                         params : {
7068                             start : this.footer.cursor + this.footer.pageSize,
7069                             limit : this.footer.pageSize
7070                         },
7071                         add : true
7072                     });
7073                 }
7074             }
7075             
7076         }
7077     },
7078     
7079     onHeaderChange : function()
7080     {
7081         var header = this.renderHeader();
7082         var table = this.el.select('table', true).first();
7083         
7084         this.mainHead.remove();
7085         this.mainHead = table.createChild(header, this.mainBody, false);
7086     },
7087     
7088     onHiddenChange : function(colModel, colIndex, hidden)
7089     {
7090         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7091         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7092         
7093         this.CSS.updateRule(thSelector, "display", "");
7094         this.CSS.updateRule(tdSelector, "display", "");
7095         
7096         if(hidden){
7097             this.CSS.updateRule(thSelector, "display", "none");
7098             this.CSS.updateRule(tdSelector, "display", "none");
7099         }
7100         
7101         this.onHeaderChange();
7102         this.onLoad();
7103         
7104     }
7105     
7106 });
7107
7108  
7109
7110  /*
7111  * - LGPL
7112  *
7113  * table cell
7114  * 
7115  */
7116
7117 /**
7118  * @class Roo.bootstrap.TableCell
7119  * @extends Roo.bootstrap.Component
7120  * Bootstrap TableCell class
7121  * @cfg {String} html cell contain text
7122  * @cfg {String} cls cell class
7123  * @cfg {String} tag cell tag (td|th) default td
7124  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7125  * @cfg {String} align Aligns the content in a cell
7126  * @cfg {String} axis Categorizes cells
7127  * @cfg {String} bgcolor Specifies the background color of a cell
7128  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7129  * @cfg {Number} colspan Specifies the number of columns a cell should span
7130  * @cfg {String} headers Specifies one or more header cells a cell is related to
7131  * @cfg {Number} height Sets the height of a cell
7132  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7133  * @cfg {Number} rowspan Sets the number of rows a cell should span
7134  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7135  * @cfg {String} valign Vertical aligns the content in a cell
7136  * @cfg {Number} width Specifies the width of a cell
7137  * 
7138  * @constructor
7139  * Create a new TableCell
7140  * @param {Object} config The config object
7141  */
7142
7143 Roo.bootstrap.TableCell = function(config){
7144     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7145 };
7146
7147 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7148     
7149     html: false,
7150     cls: false,
7151     tag: false,
7152     abbr: false,
7153     align: false,
7154     axis: false,
7155     bgcolor: false,
7156     charoff: false,
7157     colspan: false,
7158     headers: false,
7159     height: false,
7160     nowrap: false,
7161     rowspan: false,
7162     scope: false,
7163     valign: false,
7164     width: false,
7165     
7166     
7167     getAutoCreate : function(){
7168         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7169         
7170         cfg = {
7171             tag: 'td'
7172         };
7173         
7174         if(this.tag){
7175             cfg.tag = this.tag;
7176         }
7177         
7178         if (this.html) {
7179             cfg.html=this.html
7180         }
7181         if (this.cls) {
7182             cfg.cls=this.cls
7183         }
7184         if (this.abbr) {
7185             cfg.abbr=this.abbr
7186         }
7187         if (this.align) {
7188             cfg.align=this.align
7189         }
7190         if (this.axis) {
7191             cfg.axis=this.axis
7192         }
7193         if (this.bgcolor) {
7194             cfg.bgcolor=this.bgcolor
7195         }
7196         if (this.charoff) {
7197             cfg.charoff=this.charoff
7198         }
7199         if (this.colspan) {
7200             cfg.colspan=this.colspan
7201         }
7202         if (this.headers) {
7203             cfg.headers=this.headers
7204         }
7205         if (this.height) {
7206             cfg.height=this.height
7207         }
7208         if (this.nowrap) {
7209             cfg.nowrap=this.nowrap
7210         }
7211         if (this.rowspan) {
7212             cfg.rowspan=this.rowspan
7213         }
7214         if (this.scope) {
7215             cfg.scope=this.scope
7216         }
7217         if (this.valign) {
7218             cfg.valign=this.valign
7219         }
7220         if (this.width) {
7221             cfg.width=this.width
7222         }
7223         
7224         
7225         return cfg;
7226     }
7227    
7228 });
7229
7230  
7231
7232  /*
7233  * - LGPL
7234  *
7235  * table row
7236  * 
7237  */
7238
7239 /**
7240  * @class Roo.bootstrap.TableRow
7241  * @extends Roo.bootstrap.Component
7242  * Bootstrap TableRow class
7243  * @cfg {String} cls row class
7244  * @cfg {String} align Aligns the content in a table row
7245  * @cfg {String} bgcolor Specifies a background color for a table row
7246  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7247  * @cfg {String} valign Vertical aligns the content in a table row
7248  * 
7249  * @constructor
7250  * Create a new TableRow
7251  * @param {Object} config The config object
7252  */
7253
7254 Roo.bootstrap.TableRow = function(config){
7255     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7256 };
7257
7258 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7259     
7260     cls: false,
7261     align: false,
7262     bgcolor: false,
7263     charoff: false,
7264     valign: false,
7265     
7266     getAutoCreate : function(){
7267         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7268         
7269         cfg = {
7270             tag: 'tr'
7271         };
7272             
7273         if(this.cls){
7274             cfg.cls = this.cls;
7275         }
7276         if(this.align){
7277             cfg.align = this.align;
7278         }
7279         if(this.bgcolor){
7280             cfg.bgcolor = this.bgcolor;
7281         }
7282         if(this.charoff){
7283             cfg.charoff = this.charoff;
7284         }
7285         if(this.valign){
7286             cfg.valign = this.valign;
7287         }
7288         
7289         return cfg;
7290     }
7291    
7292 });
7293
7294  
7295
7296  /*
7297  * - LGPL
7298  *
7299  * table body
7300  * 
7301  */
7302
7303 /**
7304  * @class Roo.bootstrap.TableBody
7305  * @extends Roo.bootstrap.Component
7306  * Bootstrap TableBody class
7307  * @cfg {String} cls element class
7308  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7309  * @cfg {String} align Aligns the content inside the element
7310  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7311  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7312  * 
7313  * @constructor
7314  * Create a new TableBody
7315  * @param {Object} config The config object
7316  */
7317
7318 Roo.bootstrap.TableBody = function(config){
7319     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7320 };
7321
7322 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7323     
7324     cls: false,
7325     tag: false,
7326     align: false,
7327     charoff: false,
7328     valign: false,
7329     
7330     getAutoCreate : function(){
7331         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7332         
7333         cfg = {
7334             tag: 'tbody'
7335         };
7336             
7337         if (this.cls) {
7338             cfg.cls=this.cls
7339         }
7340         if(this.tag){
7341             cfg.tag = this.tag;
7342         }
7343         
7344         if(this.align){
7345             cfg.align = this.align;
7346         }
7347         if(this.charoff){
7348             cfg.charoff = this.charoff;
7349         }
7350         if(this.valign){
7351             cfg.valign = this.valign;
7352         }
7353         
7354         return cfg;
7355     }
7356     
7357     
7358 //    initEvents : function()
7359 //    {
7360 //        
7361 //        if(!this.store){
7362 //            return;
7363 //        }
7364 //        
7365 //        this.store = Roo.factory(this.store, Roo.data);
7366 //        this.store.on('load', this.onLoad, this);
7367 //        
7368 //        this.store.load();
7369 //        
7370 //    },
7371 //    
7372 //    onLoad: function () 
7373 //    {   
7374 //        this.fireEvent('load', this);
7375 //    }
7376 //    
7377 //   
7378 });
7379
7380  
7381
7382  /*
7383  * Based on:
7384  * Ext JS Library 1.1.1
7385  * Copyright(c) 2006-2007, Ext JS, LLC.
7386  *
7387  * Originally Released Under LGPL - original licence link has changed is not relivant.
7388  *
7389  * Fork - LGPL
7390  * <script type="text/javascript">
7391  */
7392
7393 // as we use this in bootstrap.
7394 Roo.namespace('Roo.form');
7395  /**
7396  * @class Roo.form.Action
7397  * Internal Class used to handle form actions
7398  * @constructor
7399  * @param {Roo.form.BasicForm} el The form element or its id
7400  * @param {Object} config Configuration options
7401  */
7402
7403  
7404  
7405 // define the action interface
7406 Roo.form.Action = function(form, options){
7407     this.form = form;
7408     this.options = options || {};
7409 };
7410 /**
7411  * Client Validation Failed
7412  * @const 
7413  */
7414 Roo.form.Action.CLIENT_INVALID = 'client';
7415 /**
7416  * Server Validation Failed
7417  * @const 
7418  */
7419 Roo.form.Action.SERVER_INVALID = 'server';
7420  /**
7421  * Connect to Server Failed
7422  * @const 
7423  */
7424 Roo.form.Action.CONNECT_FAILURE = 'connect';
7425 /**
7426  * Reading Data from Server Failed
7427  * @const 
7428  */
7429 Roo.form.Action.LOAD_FAILURE = 'load';
7430
7431 Roo.form.Action.prototype = {
7432     type : 'default',
7433     failureType : undefined,
7434     response : undefined,
7435     result : undefined,
7436
7437     // interface method
7438     run : function(options){
7439
7440     },
7441
7442     // interface method
7443     success : function(response){
7444
7445     },
7446
7447     // interface method
7448     handleResponse : function(response){
7449
7450     },
7451
7452     // default connection failure
7453     failure : function(response){
7454         
7455         this.response = response;
7456         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7457         this.form.afterAction(this, false);
7458     },
7459
7460     processResponse : function(response){
7461         this.response = response;
7462         if(!response.responseText){
7463             return true;
7464         }
7465         this.result = this.handleResponse(response);
7466         return this.result;
7467     },
7468
7469     // utility functions used internally
7470     getUrl : function(appendParams){
7471         var url = this.options.url || this.form.url || this.form.el.dom.action;
7472         if(appendParams){
7473             var p = this.getParams();
7474             if(p){
7475                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7476             }
7477         }
7478         return url;
7479     },
7480
7481     getMethod : function(){
7482         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7483     },
7484
7485     getParams : function(){
7486         var bp = this.form.baseParams;
7487         var p = this.options.params;
7488         if(p){
7489             if(typeof p == "object"){
7490                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7491             }else if(typeof p == 'string' && bp){
7492                 p += '&' + Roo.urlEncode(bp);
7493             }
7494         }else if(bp){
7495             p = Roo.urlEncode(bp);
7496         }
7497         return p;
7498     },
7499
7500     createCallback : function(){
7501         return {
7502             success: this.success,
7503             failure: this.failure,
7504             scope: this,
7505             timeout: (this.form.timeout*1000),
7506             upload: this.form.fileUpload ? this.success : undefined
7507         };
7508     }
7509 };
7510
7511 Roo.form.Action.Submit = function(form, options){
7512     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7513 };
7514
7515 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7516     type : 'submit',
7517
7518     haveProgress : false,
7519     uploadComplete : false,
7520     
7521     // uploadProgress indicator.
7522     uploadProgress : function()
7523     {
7524         if (!this.form.progressUrl) {
7525             return;
7526         }
7527         
7528         if (!this.haveProgress) {
7529             Roo.MessageBox.progress("Uploading", "Uploading");
7530         }
7531         if (this.uploadComplete) {
7532            Roo.MessageBox.hide();
7533            return;
7534         }
7535         
7536         this.haveProgress = true;
7537    
7538         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7539         
7540         var c = new Roo.data.Connection();
7541         c.request({
7542             url : this.form.progressUrl,
7543             params: {
7544                 id : uid
7545             },
7546             method: 'GET',
7547             success : function(req){
7548                //console.log(data);
7549                 var rdata = false;
7550                 var edata;
7551                 try  {
7552                    rdata = Roo.decode(req.responseText)
7553                 } catch (e) {
7554                     Roo.log("Invalid data from server..");
7555                     Roo.log(edata);
7556                     return;
7557                 }
7558                 if (!rdata || !rdata.success) {
7559                     Roo.log(rdata);
7560                     Roo.MessageBox.alert(Roo.encode(rdata));
7561                     return;
7562                 }
7563                 var data = rdata.data;
7564                 
7565                 if (this.uploadComplete) {
7566                    Roo.MessageBox.hide();
7567                    return;
7568                 }
7569                    
7570                 if (data){
7571                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7572                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7573                     );
7574                 }
7575                 this.uploadProgress.defer(2000,this);
7576             },
7577        
7578             failure: function(data) {
7579                 Roo.log('progress url failed ');
7580                 Roo.log(data);
7581             },
7582             scope : this
7583         });
7584            
7585     },
7586     
7587     
7588     run : function()
7589     {
7590         // run get Values on the form, so it syncs any secondary forms.
7591         this.form.getValues();
7592         
7593         var o = this.options;
7594         var method = this.getMethod();
7595         var isPost = method == 'POST';
7596         if(o.clientValidation === false || this.form.isValid()){
7597             
7598             if (this.form.progressUrl) {
7599                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7600                     (new Date() * 1) + '' + Math.random());
7601                     
7602             } 
7603             
7604             
7605             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7606                 form:this.form.el.dom,
7607                 url:this.getUrl(!isPost),
7608                 method: method,
7609                 params:isPost ? this.getParams() : null,
7610                 isUpload: this.form.fileUpload
7611             }));
7612             
7613             this.uploadProgress();
7614
7615         }else if (o.clientValidation !== false){ // client validation failed
7616             this.failureType = Roo.form.Action.CLIENT_INVALID;
7617             this.form.afterAction(this, false);
7618         }
7619     },
7620
7621     success : function(response)
7622     {
7623         this.uploadComplete= true;
7624         if (this.haveProgress) {
7625             Roo.MessageBox.hide();
7626         }
7627         
7628         
7629         var result = this.processResponse(response);
7630         if(result === true || result.success){
7631             this.form.afterAction(this, true);
7632             return;
7633         }
7634         if(result.errors){
7635             this.form.markInvalid(result.errors);
7636             this.failureType = Roo.form.Action.SERVER_INVALID;
7637         }
7638         this.form.afterAction(this, false);
7639     },
7640     failure : function(response)
7641     {
7642         this.uploadComplete= true;
7643         if (this.haveProgress) {
7644             Roo.MessageBox.hide();
7645         }
7646         
7647         this.response = response;
7648         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7649         this.form.afterAction(this, false);
7650     },
7651     
7652     handleResponse : function(response){
7653         if(this.form.errorReader){
7654             var rs = this.form.errorReader.read(response);
7655             var errors = [];
7656             if(rs.records){
7657                 for(var i = 0, len = rs.records.length; i < len; i++) {
7658                     var r = rs.records[i];
7659                     errors[i] = r.data;
7660                 }
7661             }
7662             if(errors.length < 1){
7663                 errors = null;
7664             }
7665             return {
7666                 success : rs.success,
7667                 errors : errors
7668             };
7669         }
7670         var ret = false;
7671         try {
7672             ret = Roo.decode(response.responseText);
7673         } catch (e) {
7674             ret = {
7675                 success: false,
7676                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7677                 errors : []
7678             };
7679         }
7680         return ret;
7681         
7682     }
7683 });
7684
7685
7686 Roo.form.Action.Load = function(form, options){
7687     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7688     this.reader = this.form.reader;
7689 };
7690
7691 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7692     type : 'load',
7693
7694     run : function(){
7695         
7696         Roo.Ajax.request(Roo.apply(
7697                 this.createCallback(), {
7698                     method:this.getMethod(),
7699                     url:this.getUrl(false),
7700                     params:this.getParams()
7701         }));
7702     },
7703
7704     success : function(response){
7705         
7706         var result = this.processResponse(response);
7707         if(result === true || !result.success || !result.data){
7708             this.failureType = Roo.form.Action.LOAD_FAILURE;
7709             this.form.afterAction(this, false);
7710             return;
7711         }
7712         this.form.clearInvalid();
7713         this.form.setValues(result.data);
7714         this.form.afterAction(this, true);
7715     },
7716
7717     handleResponse : function(response){
7718         if(this.form.reader){
7719             var rs = this.form.reader.read(response);
7720             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7721             return {
7722                 success : rs.success,
7723                 data : data
7724             };
7725         }
7726         return Roo.decode(response.responseText);
7727     }
7728 });
7729
7730 Roo.form.Action.ACTION_TYPES = {
7731     'load' : Roo.form.Action.Load,
7732     'submit' : Roo.form.Action.Submit
7733 };/*
7734  * - LGPL
7735  *
7736  * form
7737  *
7738  */
7739
7740 /**
7741  * @class Roo.bootstrap.Form
7742  * @extends Roo.bootstrap.Component
7743  * Bootstrap Form class
7744  * @cfg {String} method  GET | POST (default POST)
7745  * @cfg {String} labelAlign top | left (default top)
7746  * @cfg {String} align left  | right - for navbars
7747  * @cfg {Boolean} loadMask load mask when submit (default true)
7748
7749  *
7750  * @constructor
7751  * Create a new Form
7752  * @param {Object} config The config object
7753  */
7754
7755
7756 Roo.bootstrap.Form = function(config){
7757     
7758     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7759     
7760     Roo.bootstrap.Form.popover.apply();
7761     
7762     this.addEvents({
7763         /**
7764          * @event clientvalidation
7765          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7766          * @param {Form} this
7767          * @param {Boolean} valid true if the form has passed client-side validation
7768          */
7769         clientvalidation: true,
7770         /**
7771          * @event beforeaction
7772          * Fires before any action is performed. Return false to cancel the action.
7773          * @param {Form} this
7774          * @param {Action} action The action to be performed
7775          */
7776         beforeaction: true,
7777         /**
7778          * @event actionfailed
7779          * Fires when an action fails.
7780          * @param {Form} this
7781          * @param {Action} action The action that failed
7782          */
7783         actionfailed : true,
7784         /**
7785          * @event actioncomplete
7786          * Fires when an action is completed.
7787          * @param {Form} this
7788          * @param {Action} action The action that completed
7789          */
7790         actioncomplete : true
7791     });
7792 };
7793
7794 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7795
7796      /**
7797      * @cfg {String} method
7798      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7799      */
7800     method : 'POST',
7801     /**
7802      * @cfg {String} url
7803      * The URL to use for form actions if one isn't supplied in the action options.
7804      */
7805     /**
7806      * @cfg {Boolean} fileUpload
7807      * Set to true if this form is a file upload.
7808      */
7809
7810     /**
7811      * @cfg {Object} baseParams
7812      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7813      */
7814
7815     /**
7816      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7817      */
7818     timeout: 30,
7819     /**
7820      * @cfg {Sting} align (left|right) for navbar forms
7821      */
7822     align : 'left',
7823
7824     // private
7825     activeAction : null,
7826
7827     /**
7828      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7829      * element by passing it or its id or mask the form itself by passing in true.
7830      * @type Mixed
7831      */
7832     waitMsgTarget : false,
7833
7834     loadMask : true,
7835     
7836     /**
7837      * @cfg {Boolean} errorMask (true|false) default false
7838      */
7839     errorMask : false,
7840     
7841     /**
7842      * @cfg {Number} maskOffset Default 100
7843      */
7844     maskOffset : 100,
7845     
7846     /**
7847      * @cfg {Boolean} maskBody
7848      */
7849     maskBody : false,
7850
7851     getAutoCreate : function(){
7852
7853         var cfg = {
7854             tag: 'form',
7855             method : this.method || 'POST',
7856             id : this.id || Roo.id(),
7857             cls : ''
7858         };
7859         if (this.parent().xtype.match(/^Nav/)) {
7860             cfg.cls = 'navbar-form navbar-' + this.align;
7861
7862         }
7863
7864         if (this.labelAlign == 'left' ) {
7865             cfg.cls += ' form-horizontal';
7866         }
7867
7868
7869         return cfg;
7870     },
7871     initEvents : function()
7872     {
7873         this.el.on('submit', this.onSubmit, this);
7874         // this was added as random key presses on the form where triggering form submit.
7875         this.el.on('keypress', function(e) {
7876             if (e.getCharCode() != 13) {
7877                 return true;
7878             }
7879             // we might need to allow it for textareas.. and some other items.
7880             // check e.getTarget().
7881
7882             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7883                 return true;
7884             }
7885
7886             Roo.log("keypress blocked");
7887
7888             e.preventDefault();
7889             return false;
7890         });
7891         
7892     },
7893     // private
7894     onSubmit : function(e){
7895         e.stopEvent();
7896     },
7897
7898      /**
7899      * Returns true if client-side validation on the form is successful.
7900      * @return Boolean
7901      */
7902     isValid : function(){
7903         var items = this.getItems();
7904         var valid = true;
7905         var target = false;
7906         
7907         items.each(function(f){
7908             
7909             if(f.validate()){
7910                 return;
7911             }
7912             
7913             Roo.log('invalid field: ' + f.name);
7914             
7915             valid = false;
7916
7917             if(!target && f.el.isVisible(true)){
7918                 target = f;
7919             }
7920            
7921         });
7922         
7923         if(this.errorMask && !valid){
7924             Roo.bootstrap.Form.popover.mask(this, target);
7925         }
7926         
7927         return valid;
7928     },
7929     
7930     /**
7931      * Returns true if any fields in this form have changed since their original load.
7932      * @return Boolean
7933      */
7934     isDirty : function(){
7935         var dirty = false;
7936         var items = this.getItems();
7937         items.each(function(f){
7938            if(f.isDirty()){
7939                dirty = true;
7940                return false;
7941            }
7942            return true;
7943         });
7944         return dirty;
7945     },
7946      /**
7947      * Performs a predefined action (submit or load) or custom actions you define on this form.
7948      * @param {String} actionName The name of the action type
7949      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7950      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7951      * accept other config options):
7952      * <pre>
7953 Property          Type             Description
7954 ----------------  ---------------  ----------------------------------------------------------------------------------
7955 url               String           The url for the action (defaults to the form's url)
7956 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7957 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7958 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7959                                    validate the form on the client (defaults to false)
7960      * </pre>
7961      * @return {BasicForm} this
7962      */
7963     doAction : function(action, options){
7964         if(typeof action == 'string'){
7965             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7966         }
7967         if(this.fireEvent('beforeaction', this, action) !== false){
7968             this.beforeAction(action);
7969             action.run.defer(100, action);
7970         }
7971         return this;
7972     },
7973
7974     // private
7975     beforeAction : function(action){
7976         var o = action.options;
7977         
7978         if(this.loadMask){
7979             
7980             if(this.maskBody){
7981                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7982             } else {
7983                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7984             }
7985         }
7986         // not really supported yet.. ??
7987
7988         //if(this.waitMsgTarget === true){
7989         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7990         //}else if(this.waitMsgTarget){
7991         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7992         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7993         //}else {
7994         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7995        // }
7996
7997     },
7998
7999     // private
8000     afterAction : function(action, success){
8001         this.activeAction = null;
8002         var o = action.options;
8003
8004         if(this.loadMask){
8005             
8006             if(this.maskBody){
8007                 Roo.get(document.body).unmask();
8008             } else {
8009                 this.el.unmask();
8010             }
8011         }
8012         
8013         //if(this.waitMsgTarget === true){
8014 //            this.el.unmask();
8015         //}else if(this.waitMsgTarget){
8016         //    this.waitMsgTarget.unmask();
8017         //}else{
8018         //    Roo.MessageBox.updateProgress(1);
8019         //    Roo.MessageBox.hide();
8020        // }
8021         //
8022         if(success){
8023             if(o.reset){
8024                 this.reset();
8025             }
8026             Roo.callback(o.success, o.scope, [this, action]);
8027             this.fireEvent('actioncomplete', this, action);
8028
8029         }else{
8030
8031             // failure condition..
8032             // we have a scenario where updates need confirming.
8033             // eg. if a locking scenario exists..
8034             // we look for { errors : { needs_confirm : true }} in the response.
8035             if (
8036                 (typeof(action.result) != 'undefined')  &&
8037                 (typeof(action.result.errors) != 'undefined')  &&
8038                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8039            ){
8040                 var _t = this;
8041                 Roo.log("not supported yet");
8042                  /*
8043
8044                 Roo.MessageBox.confirm(
8045                     "Change requires confirmation",
8046                     action.result.errorMsg,
8047                     function(r) {
8048                         if (r != 'yes') {
8049                             return;
8050                         }
8051                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8052                     }
8053
8054                 );
8055                 */
8056
8057
8058                 return;
8059             }
8060
8061             Roo.callback(o.failure, o.scope, [this, action]);
8062             // show an error message if no failed handler is set..
8063             if (!this.hasListener('actionfailed')) {
8064                 Roo.log("need to add dialog support");
8065                 /*
8066                 Roo.MessageBox.alert("Error",
8067                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8068                         action.result.errorMsg :
8069                         "Saving Failed, please check your entries or try again"
8070                 );
8071                 */
8072             }
8073
8074             this.fireEvent('actionfailed', this, action);
8075         }
8076
8077     },
8078     /**
8079      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8080      * @param {String} id The value to search for
8081      * @return Field
8082      */
8083     findField : function(id){
8084         var items = this.getItems();
8085         var field = items.get(id);
8086         if(!field){
8087              items.each(function(f){
8088                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8089                     field = f;
8090                     return false;
8091                 }
8092                 return true;
8093             });
8094         }
8095         return field || null;
8096     },
8097      /**
8098      * Mark fields in this form invalid in bulk.
8099      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8100      * @return {BasicForm} this
8101      */
8102     markInvalid : function(errors){
8103         if(errors instanceof Array){
8104             for(var i = 0, len = errors.length; i < len; i++){
8105                 var fieldError = errors[i];
8106                 var f = this.findField(fieldError.id);
8107                 if(f){
8108                     f.markInvalid(fieldError.msg);
8109                 }
8110             }
8111         }else{
8112             var field, id;
8113             for(id in errors){
8114                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8115                     field.markInvalid(errors[id]);
8116                 }
8117             }
8118         }
8119         //Roo.each(this.childForms || [], function (f) {
8120         //    f.markInvalid(errors);
8121         //});
8122
8123         return this;
8124     },
8125
8126     /**
8127      * Set values for fields in this form in bulk.
8128      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8129      * @return {BasicForm} this
8130      */
8131     setValues : function(values){
8132         if(values instanceof Array){ // array of objects
8133             for(var i = 0, len = values.length; i < len; i++){
8134                 var v = values[i];
8135                 var f = this.findField(v.id);
8136                 if(f){
8137                     f.setValue(v.value);
8138                     if(this.trackResetOnLoad){
8139                         f.originalValue = f.getValue();
8140                     }
8141                 }
8142             }
8143         }else{ // object hash
8144             var field, id;
8145             for(id in values){
8146                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8147
8148                     if (field.setFromData &&
8149                         field.valueField &&
8150                         field.displayField &&
8151                         // combos' with local stores can
8152                         // be queried via setValue()
8153                         // to set their value..
8154                         (field.store && !field.store.isLocal)
8155                         ) {
8156                         // it's a combo
8157                         var sd = { };
8158                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8159                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8160                         field.setFromData(sd);
8161
8162                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8163                         
8164                         field.setFromData(values);
8165                         
8166                     } else {
8167                         field.setValue(values[id]);
8168                     }
8169
8170
8171                     if(this.trackResetOnLoad){
8172                         field.originalValue = field.getValue();
8173                     }
8174                 }
8175             }
8176         }
8177
8178         //Roo.each(this.childForms || [], function (f) {
8179         //    f.setValues(values);
8180         //});
8181
8182         return this;
8183     },
8184
8185     /**
8186      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8187      * they are returned as an array.
8188      * @param {Boolean} asString
8189      * @return {Object}
8190      */
8191     getValues : function(asString){
8192         //if (this.childForms) {
8193             // copy values from the child forms
8194         //    Roo.each(this.childForms, function (f) {
8195         //        this.setValues(f.getValues());
8196         //    }, this);
8197         //}
8198
8199
8200
8201         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8202         if(asString === true){
8203             return fs;
8204         }
8205         return Roo.urlDecode(fs);
8206     },
8207
8208     /**
8209      * Returns the fields in this form as an object with key/value pairs.
8210      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8211      * @return {Object}
8212      */
8213     getFieldValues : function(with_hidden)
8214     {
8215         var items = this.getItems();
8216         var ret = {};
8217         items.each(function(f){
8218             
8219             if (!f.getName()) {
8220                 return;
8221             }
8222             
8223             var v = f.getValue();
8224             
8225             if (f.inputType =='radio') {
8226                 if (typeof(ret[f.getName()]) == 'undefined') {
8227                     ret[f.getName()] = ''; // empty..
8228                 }
8229
8230                 if (!f.el.dom.checked) {
8231                     return;
8232
8233                 }
8234                 v = f.el.dom.value;
8235
8236             }
8237             
8238             if(f.xtype == 'MoneyField'){
8239                 ret[f.currencyName] = f.getCurrency();
8240             }
8241
8242             // not sure if this supported any more..
8243             if ((typeof(v) == 'object') && f.getRawValue) {
8244                 v = f.getRawValue() ; // dates..
8245             }
8246             // combo boxes where name != hiddenName...
8247             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8248                 ret[f.name] = f.getRawValue();
8249             }
8250             ret[f.getName()] = v;
8251         });
8252
8253         return ret;
8254     },
8255
8256     /**
8257      * Clears all invalid messages in this form.
8258      * @return {BasicForm} this
8259      */
8260     clearInvalid : function(){
8261         var items = this.getItems();
8262
8263         items.each(function(f){
8264            f.clearInvalid();
8265         });
8266
8267         return this;
8268     },
8269
8270     /**
8271      * Resets this form.
8272      * @return {BasicForm} this
8273      */
8274     reset : function(){
8275         var items = this.getItems();
8276         items.each(function(f){
8277             f.reset();
8278         });
8279
8280         Roo.each(this.childForms || [], function (f) {
8281             f.reset();
8282         });
8283
8284
8285         return this;
8286     },
8287     
8288     getItems : function()
8289     {
8290         var r=new Roo.util.MixedCollection(false, function(o){
8291             return o.id || (o.id = Roo.id());
8292         });
8293         var iter = function(el) {
8294             if (el.inputEl) {
8295                 r.add(el);
8296             }
8297             if (!el.items) {
8298                 return;
8299             }
8300             Roo.each(el.items,function(e) {
8301                 iter(e);
8302             });
8303         };
8304
8305         iter(this);
8306         return r;
8307     },
8308     
8309     hideFields : function(items)
8310     {
8311         Roo.each(items, function(i){
8312             
8313             var f = this.findField(i);
8314             
8315             if(!f){
8316                 return;
8317             }
8318             
8319             if(f.xtype == 'DateField'){
8320                 f.setVisible(false);
8321                 return;
8322             }
8323             
8324             f.hide();
8325             
8326         }, this);
8327     },
8328     
8329     showFields : function(items)
8330     {
8331         Roo.each(items, function(i){
8332             
8333             var f = this.findField(i);
8334             
8335             if(!f){
8336                 return;
8337             }
8338             
8339             if(f.xtype == 'DateField'){
8340                 f.setVisible(true);
8341                 return;
8342             }
8343             
8344             f.show();
8345             
8346         }, this);
8347     }
8348
8349 });
8350
8351 Roo.apply(Roo.bootstrap.Form, {
8352     
8353     popover : {
8354         
8355         padding : 5,
8356         
8357         isApplied : false,
8358         
8359         isMasked : false,
8360         
8361         form : false,
8362         
8363         target : false,
8364         
8365         toolTip : false,
8366         
8367         intervalID : false,
8368         
8369         maskEl : false,
8370         
8371         apply : function()
8372         {
8373             if(this.isApplied){
8374                 return;
8375             }
8376             
8377             this.maskEl = {
8378                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8379                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8380                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8381                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8382             };
8383             
8384             this.maskEl.top.enableDisplayMode("block");
8385             this.maskEl.left.enableDisplayMode("block");
8386             this.maskEl.bottom.enableDisplayMode("block");
8387             this.maskEl.right.enableDisplayMode("block");
8388             
8389             this.toolTip = new Roo.bootstrap.Tooltip({
8390                 cls : 'roo-form-error-popover',
8391                 alignment : {
8392                     'left' : ['r-l', [-2,0], 'right'],
8393                     'right' : ['l-r', [2,0], 'left'],
8394                     'bottom' : ['tl-bl', [0,2], 'top'],
8395                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8396                 }
8397             });
8398             
8399             this.toolTip.render(Roo.get(document.body));
8400
8401             this.toolTip.el.enableDisplayMode("block");
8402             
8403             Roo.get(document.body).on('click', function(){
8404                 this.unmask();
8405             }, this);
8406             
8407             Roo.get(document.body).on('touchstart', function(){
8408                 this.unmask();
8409             }, this);
8410             
8411             this.isApplied = true
8412         },
8413         
8414         mask : function(form, target)
8415         {
8416             this.form = form;
8417             
8418             this.target = target;
8419             
8420             if(!this.form.errorMask || !target.el){
8421                 return;
8422             }
8423             
8424             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8425             
8426             Roo.log(scrollable);
8427             
8428             var ot = this.target.el.calcOffsetsTo(scrollable);
8429             
8430             var scrollTo = ot[1] - this.form.maskOffset;
8431             
8432             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8433             
8434             scrollable.scrollTo('top', scrollTo);
8435             
8436             var box = this.target.el.getBox();
8437             Roo.log(box);
8438             var zIndex = Roo.bootstrap.Modal.zIndex++;
8439
8440             
8441             this.maskEl.top.setStyle('position', 'absolute');
8442             this.maskEl.top.setStyle('z-index', zIndex);
8443             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8444             this.maskEl.top.setLeft(0);
8445             this.maskEl.top.setTop(0);
8446             this.maskEl.top.show();
8447             
8448             this.maskEl.left.setStyle('position', 'absolute');
8449             this.maskEl.left.setStyle('z-index', zIndex);
8450             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8451             this.maskEl.left.setLeft(0);
8452             this.maskEl.left.setTop(box.y - this.padding);
8453             this.maskEl.left.show();
8454
8455             this.maskEl.bottom.setStyle('position', 'absolute');
8456             this.maskEl.bottom.setStyle('z-index', zIndex);
8457             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8458             this.maskEl.bottom.setLeft(0);
8459             this.maskEl.bottom.setTop(box.bottom + this.padding);
8460             this.maskEl.bottom.show();
8461
8462             this.maskEl.right.setStyle('position', 'absolute');
8463             this.maskEl.right.setStyle('z-index', zIndex);
8464             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8465             this.maskEl.right.setLeft(box.right + this.padding);
8466             this.maskEl.right.setTop(box.y - this.padding);
8467             this.maskEl.right.show();
8468
8469             this.toolTip.bindEl = this.target.el;
8470
8471             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8472
8473             var tip = this.target.blankText;
8474
8475             if(this.target.getValue() !== '' ) {
8476                 
8477                 if (this.target.invalidText.length) {
8478                     tip = this.target.invalidText;
8479                 } else if (this.target.regexText.length){
8480                     tip = this.target.regexText;
8481                 }
8482             }
8483
8484             this.toolTip.show(tip);
8485
8486             this.intervalID = window.setInterval(function() {
8487                 Roo.bootstrap.Form.popover.unmask();
8488             }, 10000);
8489
8490             window.onwheel = function(){ return false;};
8491             
8492             (function(){ this.isMasked = true; }).defer(500, this);
8493             
8494         },
8495         
8496         unmask : function()
8497         {
8498             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8499                 return;
8500             }
8501             
8502             this.maskEl.top.setStyle('position', 'absolute');
8503             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8504             this.maskEl.top.hide();
8505
8506             this.maskEl.left.setStyle('position', 'absolute');
8507             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8508             this.maskEl.left.hide();
8509
8510             this.maskEl.bottom.setStyle('position', 'absolute');
8511             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8512             this.maskEl.bottom.hide();
8513
8514             this.maskEl.right.setStyle('position', 'absolute');
8515             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8516             this.maskEl.right.hide();
8517             
8518             this.toolTip.hide();
8519             
8520             this.toolTip.el.hide();
8521             
8522             window.onwheel = function(){ return true;};
8523             
8524             if(this.intervalID){
8525                 window.clearInterval(this.intervalID);
8526                 this.intervalID = false;
8527             }
8528             
8529             this.isMasked = false;
8530             
8531         }
8532         
8533     }
8534     
8535 });
8536
8537 /*
8538  * Based on:
8539  * Ext JS Library 1.1.1
8540  * Copyright(c) 2006-2007, Ext JS, LLC.
8541  *
8542  * Originally Released Under LGPL - original licence link has changed is not relivant.
8543  *
8544  * Fork - LGPL
8545  * <script type="text/javascript">
8546  */
8547 /**
8548  * @class Roo.form.VTypes
8549  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8550  * @singleton
8551  */
8552 Roo.form.VTypes = function(){
8553     // closure these in so they are only created once.
8554     var alpha = /^[a-zA-Z_]+$/;
8555     var alphanum = /^[a-zA-Z0-9_]+$/;
8556     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8557     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8558
8559     // All these messages and functions are configurable
8560     return {
8561         /**
8562          * The function used to validate email addresses
8563          * @param {String} value The email address
8564          */
8565         'email' : function(v){
8566             return email.test(v);
8567         },
8568         /**
8569          * The error text to display when the email validation function returns false
8570          * @type String
8571          */
8572         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8573         /**
8574          * The keystroke filter mask to be applied on email input
8575          * @type RegExp
8576          */
8577         'emailMask' : /[a-z0-9_\.\-@]/i,
8578
8579         /**
8580          * The function used to validate URLs
8581          * @param {String} value The URL
8582          */
8583         'url' : function(v){
8584             return url.test(v);
8585         },
8586         /**
8587          * The error text to display when the url validation function returns false
8588          * @type String
8589          */
8590         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8591         
8592         /**
8593          * The function used to validate alpha values
8594          * @param {String} value The value
8595          */
8596         'alpha' : function(v){
8597             return alpha.test(v);
8598         },
8599         /**
8600          * The error text to display when the alpha validation function returns false
8601          * @type String
8602          */
8603         'alphaText' : 'This field should only contain letters and _',
8604         /**
8605          * The keystroke filter mask to be applied on alpha input
8606          * @type RegExp
8607          */
8608         'alphaMask' : /[a-z_]/i,
8609
8610         /**
8611          * The function used to validate alphanumeric values
8612          * @param {String} value The value
8613          */
8614         'alphanum' : function(v){
8615             return alphanum.test(v);
8616         },
8617         /**
8618          * The error text to display when the alphanumeric validation function returns false
8619          * @type String
8620          */
8621         'alphanumText' : 'This field should only contain letters, numbers and _',
8622         /**
8623          * The keystroke filter mask to be applied on alphanumeric input
8624          * @type RegExp
8625          */
8626         'alphanumMask' : /[a-z0-9_]/i
8627     };
8628 }();/*
8629  * - LGPL
8630  *
8631  * Input
8632  * 
8633  */
8634
8635 /**
8636  * @class Roo.bootstrap.Input
8637  * @extends Roo.bootstrap.Component
8638  * Bootstrap Input class
8639  * @cfg {Boolean} disabled is it disabled
8640  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8641  * @cfg {String} name name of the input
8642  * @cfg {string} fieldLabel - the label associated
8643  * @cfg {string} placeholder - placeholder to put in text.
8644  * @cfg {string}  before - input group add on before
8645  * @cfg {string} after - input group add on after
8646  * @cfg {string} size - (lg|sm) or leave empty..
8647  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8648  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8649  * @cfg {Number} md colspan out of 12 for computer-sized screens
8650  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8651  * @cfg {string} value default value of the input
8652  * @cfg {Number} labelWidth set the width of label 
8653  * @cfg {Number} labellg set the width of label (1-12)
8654  * @cfg {Number} labelmd set the width of label (1-12)
8655  * @cfg {Number} labelsm set the width of label (1-12)
8656  * @cfg {Number} labelxs set the width of label (1-12)
8657  * @cfg {String} labelAlign (top|left)
8658  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8659  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8660  * @cfg {String} indicatorpos (left|right) default left
8661  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8662  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8663
8664  * @cfg {String} align (left|center|right) Default left
8665  * @cfg {Boolean} forceFeedback (true|false) Default false
8666  * 
8667  * @constructor
8668  * Create a new Input
8669  * @param {Object} config The config object
8670  */
8671
8672 Roo.bootstrap.Input = function(config){
8673     
8674     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8675     
8676     this.addEvents({
8677         /**
8678          * @event focus
8679          * Fires when this field receives input focus.
8680          * @param {Roo.form.Field} this
8681          */
8682         focus : true,
8683         /**
8684          * @event blur
8685          * Fires when this field loses input focus.
8686          * @param {Roo.form.Field} this
8687          */
8688         blur : true,
8689         /**
8690          * @event specialkey
8691          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8692          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8693          * @param {Roo.form.Field} this
8694          * @param {Roo.EventObject} e The event object
8695          */
8696         specialkey : true,
8697         /**
8698          * @event change
8699          * Fires just before the field blurs if the field value has changed.
8700          * @param {Roo.form.Field} this
8701          * @param {Mixed} newValue The new value
8702          * @param {Mixed} oldValue The original value
8703          */
8704         change : true,
8705         /**
8706          * @event invalid
8707          * Fires after the field has been marked as invalid.
8708          * @param {Roo.form.Field} this
8709          * @param {String} msg The validation message
8710          */
8711         invalid : true,
8712         /**
8713          * @event valid
8714          * Fires after the field has been validated with no errors.
8715          * @param {Roo.form.Field} this
8716          */
8717         valid : true,
8718          /**
8719          * @event keyup
8720          * Fires after the key up
8721          * @param {Roo.form.Field} this
8722          * @param {Roo.EventObject}  e The event Object
8723          */
8724         keyup : true
8725     });
8726 };
8727
8728 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8729      /**
8730      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8731       automatic validation (defaults to "keyup").
8732      */
8733     validationEvent : "keyup",
8734      /**
8735      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8736      */
8737     validateOnBlur : true,
8738     /**
8739      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8740      */
8741     validationDelay : 250,
8742      /**
8743      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8744      */
8745     focusClass : "x-form-focus",  // not needed???
8746     
8747        
8748     /**
8749      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8750      */
8751     invalidClass : "has-warning",
8752     
8753     /**
8754      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8755      */
8756     validClass : "has-success",
8757     
8758     /**
8759      * @cfg {Boolean} hasFeedback (true|false) default true
8760      */
8761     hasFeedback : true,
8762     
8763     /**
8764      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8765      */
8766     invalidFeedbackClass : "glyphicon-warning-sign",
8767     
8768     /**
8769      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8770      */
8771     validFeedbackClass : "glyphicon-ok",
8772     
8773     /**
8774      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8775      */
8776     selectOnFocus : false,
8777     
8778      /**
8779      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8780      */
8781     maskRe : null,
8782        /**
8783      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8784      */
8785     vtype : null,
8786     
8787       /**
8788      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8789      */
8790     disableKeyFilter : false,
8791     
8792        /**
8793      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8794      */
8795     disabled : false,
8796      /**
8797      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8798      */
8799     allowBlank : true,
8800     /**
8801      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8802      */
8803     blankText : "Please complete this mandatory field",
8804     
8805      /**
8806      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8807      */
8808     minLength : 0,
8809     /**
8810      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8811      */
8812     maxLength : Number.MAX_VALUE,
8813     /**
8814      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8815      */
8816     minLengthText : "The minimum length for this field is {0}",
8817     /**
8818      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8819      */
8820     maxLengthText : "The maximum length for this field is {0}",
8821   
8822     
8823     /**
8824      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8825      * If available, this function will be called only after the basic validators all return true, and will be passed the
8826      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8827      */
8828     validator : null,
8829     /**
8830      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8831      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8832      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8833      */
8834     regex : null,
8835     /**
8836      * @cfg {String} regexText -- Depricated - use Invalid Text
8837      */
8838     regexText : "",
8839     
8840     /**
8841      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8842      */
8843     invalidText : "",
8844     
8845     
8846     
8847     autocomplete: false,
8848     
8849     
8850     fieldLabel : '',
8851     inputType : 'text',
8852     
8853     name : false,
8854     placeholder: false,
8855     before : false,
8856     after : false,
8857     size : false,
8858     hasFocus : false,
8859     preventMark: false,
8860     isFormField : true,
8861     value : '',
8862     labelWidth : 2,
8863     labelAlign : false,
8864     readOnly : false,
8865     align : false,
8866     formatedValue : false,
8867     forceFeedback : false,
8868     
8869     indicatorpos : 'left',
8870     
8871     labellg : 0,
8872     labelmd : 0,
8873     labelsm : 0,
8874     labelxs : 0,
8875     
8876     capture : '',
8877     accept : '',
8878     
8879     parentLabelAlign : function()
8880     {
8881         var parent = this;
8882         while (parent.parent()) {
8883             parent = parent.parent();
8884             if (typeof(parent.labelAlign) !='undefined') {
8885                 return parent.labelAlign;
8886             }
8887         }
8888         return 'left';
8889         
8890     },
8891     
8892     getAutoCreate : function()
8893     {
8894         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8895         
8896         var id = Roo.id();
8897         
8898         var cfg = {};
8899         
8900         if(this.inputType != 'hidden'){
8901             cfg.cls = 'form-group' //input-group
8902         }
8903         
8904         var input =  {
8905             tag: 'input',
8906             id : id,
8907             type : this.inputType,
8908             value : this.value,
8909             cls : 'form-control',
8910             placeholder : this.placeholder || '',
8911             autocomplete : this.autocomplete || 'new-password'
8912         };
8913         
8914         if(this.capture.length){
8915             input.capture = this.capture;
8916         }
8917         
8918         if(this.accept.length){
8919             input.accept = this.accept + "/*";
8920         }
8921         
8922         if(this.align){
8923             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8924         }
8925         
8926         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8927             input.maxLength = this.maxLength;
8928         }
8929         
8930         if (this.disabled) {
8931             input.disabled=true;
8932         }
8933         
8934         if (this.readOnly) {
8935             input.readonly=true;
8936         }
8937         
8938         if (this.name) {
8939             input.name = this.name;
8940         }
8941         
8942         if (this.size) {
8943             input.cls += ' input-' + this.size;
8944         }
8945         
8946         var settings=this;
8947         ['xs','sm','md','lg'].map(function(size){
8948             if (settings[size]) {
8949                 cfg.cls += ' col-' + size + '-' + settings[size];
8950             }
8951         });
8952         
8953         var inputblock = input;
8954         
8955         var feedback = {
8956             tag: 'span',
8957             cls: 'glyphicon form-control-feedback'
8958         };
8959             
8960         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8961             
8962             inputblock = {
8963                 cls : 'has-feedback',
8964                 cn :  [
8965                     input,
8966                     feedback
8967                 ] 
8968             };  
8969         }
8970         
8971         if (this.before || this.after) {
8972             
8973             inputblock = {
8974                 cls : 'input-group',
8975                 cn :  [] 
8976             };
8977             
8978             if (this.before && typeof(this.before) == 'string') {
8979                 
8980                 inputblock.cn.push({
8981                     tag :'span',
8982                     cls : 'roo-input-before input-group-addon',
8983                     html : this.before
8984                 });
8985             }
8986             if (this.before && typeof(this.before) == 'object') {
8987                 this.before = Roo.factory(this.before);
8988                 
8989                 inputblock.cn.push({
8990                     tag :'span',
8991                     cls : 'roo-input-before input-group-' +
8992                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8993                 });
8994             }
8995             
8996             inputblock.cn.push(input);
8997             
8998             if (this.after && typeof(this.after) == 'string') {
8999                 inputblock.cn.push({
9000                     tag :'span',
9001                     cls : 'roo-input-after input-group-addon',
9002                     html : this.after
9003                 });
9004             }
9005             if (this.after && typeof(this.after) == 'object') {
9006                 this.after = Roo.factory(this.after);
9007                 
9008                 inputblock.cn.push({
9009                     tag :'span',
9010                     cls : 'roo-input-after input-group-' +
9011                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9012                 });
9013             }
9014             
9015             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9016                 inputblock.cls += ' has-feedback';
9017                 inputblock.cn.push(feedback);
9018             }
9019         };
9020         
9021         if (align ==='left' && this.fieldLabel.length) {
9022             
9023             cfg.cls += ' roo-form-group-label-left';
9024             
9025             cfg.cn = [
9026                 {
9027                     tag : 'i',
9028                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9029                     tooltip : 'This field is required'
9030                 },
9031                 {
9032                     tag: 'label',
9033                     'for' :  id,
9034                     cls : 'control-label',
9035                     html : this.fieldLabel
9036
9037                 },
9038                 {
9039                     cls : "", 
9040                     cn: [
9041                         inputblock
9042                     ]
9043                 }
9044             ];
9045             
9046             var labelCfg = cfg.cn[1];
9047             var contentCfg = cfg.cn[2];
9048             
9049             if(this.indicatorpos == 'right'){
9050                 cfg.cn = [
9051                     {
9052                         tag: 'label',
9053                         'for' :  id,
9054                         cls : 'control-label',
9055                         cn : [
9056                             {
9057                                 tag : 'span',
9058                                 html : this.fieldLabel
9059                             },
9060                             {
9061                                 tag : 'i',
9062                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9063                                 tooltip : 'This field is required'
9064                             }
9065                         ]
9066                     },
9067                     {
9068                         cls : "",
9069                         cn: [
9070                             inputblock
9071                         ]
9072                     }
9073
9074                 ];
9075                 
9076                 labelCfg = cfg.cn[0];
9077                 contentCfg = cfg.cn[1];
9078             
9079             }
9080             
9081             if(this.labelWidth > 12){
9082                 labelCfg.style = "width: " + this.labelWidth + 'px';
9083             }
9084             
9085             if(this.labelWidth < 13 && this.labelmd == 0){
9086                 this.labelmd = this.labelWidth;
9087             }
9088             
9089             if(this.labellg > 0){
9090                 labelCfg.cls += ' col-lg-' + this.labellg;
9091                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9092             }
9093             
9094             if(this.labelmd > 0){
9095                 labelCfg.cls += ' col-md-' + this.labelmd;
9096                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9097             }
9098             
9099             if(this.labelsm > 0){
9100                 labelCfg.cls += ' col-sm-' + this.labelsm;
9101                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9102             }
9103             
9104             if(this.labelxs > 0){
9105                 labelCfg.cls += ' col-xs-' + this.labelxs;
9106                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9107             }
9108             
9109             
9110         } else if ( this.fieldLabel.length) {
9111                 
9112             cfg.cn = [
9113                 {
9114                     tag : 'i',
9115                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9116                     tooltip : 'This field is required'
9117                 },
9118                 {
9119                     tag: 'label',
9120                    //cls : 'input-group-addon',
9121                     html : this.fieldLabel
9122
9123                 },
9124
9125                inputblock
9126
9127            ];
9128            
9129            if(this.indicatorpos == 'right'){
9130                 
9131                 cfg.cn = [
9132                     {
9133                         tag: 'label',
9134                        //cls : 'input-group-addon',
9135                         html : this.fieldLabel
9136
9137                     },
9138                     {
9139                         tag : 'i',
9140                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9141                         tooltip : 'This field is required'
9142                     },
9143
9144                    inputblock
9145
9146                ];
9147
9148             }
9149
9150         } else {
9151             
9152             cfg.cn = [
9153
9154                     inputblock
9155
9156             ];
9157                 
9158                 
9159         };
9160         
9161         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9162            cfg.cls += ' navbar-form';
9163         }
9164         
9165         if (this.parentType === 'NavGroup') {
9166            cfg.cls += ' navbar-form';
9167            cfg.tag = 'li';
9168         }
9169         
9170         return cfg;
9171         
9172     },
9173     /**
9174      * return the real input element.
9175      */
9176     inputEl: function ()
9177     {
9178         return this.el.select('input.form-control',true).first();
9179     },
9180     
9181     tooltipEl : function()
9182     {
9183         return this.inputEl();
9184     },
9185     
9186     indicatorEl : function()
9187     {
9188         var indicator = this.el.select('i.roo-required-indicator',true).first();
9189         
9190         if(!indicator){
9191             return false;
9192         }
9193         
9194         return indicator;
9195         
9196     },
9197     
9198     setDisabled : function(v)
9199     {
9200         var i  = this.inputEl().dom;
9201         if (!v) {
9202             i.removeAttribute('disabled');
9203             return;
9204             
9205         }
9206         i.setAttribute('disabled','true');
9207     },
9208     initEvents : function()
9209     {
9210           
9211         this.inputEl().on("keydown" , this.fireKey,  this);
9212         this.inputEl().on("focus", this.onFocus,  this);
9213         this.inputEl().on("blur", this.onBlur,  this);
9214         
9215         this.inputEl().relayEvent('keyup', this);
9216         
9217         this.indicator = this.indicatorEl();
9218         
9219         if(this.indicator){
9220             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9221         }
9222  
9223         // reference to original value for reset
9224         this.originalValue = this.getValue();
9225         //Roo.form.TextField.superclass.initEvents.call(this);
9226         if(this.validationEvent == 'keyup'){
9227             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9228             this.inputEl().on('keyup', this.filterValidation, this);
9229         }
9230         else if(this.validationEvent !== false){
9231             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9232         }
9233         
9234         if(this.selectOnFocus){
9235             this.on("focus", this.preFocus, this);
9236             
9237         }
9238         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9239             this.inputEl().on("keypress", this.filterKeys, this);
9240         } else {
9241             this.inputEl().relayEvent('keypress', this);
9242         }
9243        /* if(this.grow){
9244             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9245             this.el.on("click", this.autoSize,  this);
9246         }
9247         */
9248         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9249             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9250         }
9251         
9252         if (typeof(this.before) == 'object') {
9253             this.before.render(this.el.select('.roo-input-before',true).first());
9254         }
9255         if (typeof(this.after) == 'object') {
9256             this.after.render(this.el.select('.roo-input-after',true).first());
9257         }
9258         
9259         this.inputEl().on('change', this.onChange, this);
9260         
9261     },
9262     filterValidation : function(e){
9263         if(!e.isNavKeyPress()){
9264             this.validationTask.delay(this.validationDelay);
9265         }
9266     },
9267      /**
9268      * Validates the field value
9269      * @return {Boolean} True if the value is valid, else false
9270      */
9271     validate : function(){
9272         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9273         if(this.disabled || this.validateValue(this.getRawValue())){
9274             this.markValid();
9275             return true;
9276         }
9277         
9278         this.markInvalid();
9279         return false;
9280     },
9281     
9282     
9283     /**
9284      * Validates a value according to the field's validation rules and marks the field as invalid
9285      * if the validation fails
9286      * @param {Mixed} value The value to validate
9287      * @return {Boolean} True if the value is valid, else false
9288      */
9289     validateValue : function(value)
9290     {
9291         if(this.getVisibilityEl().hasClass('hidden')){
9292             return true;
9293         }
9294         
9295         if(value.length < 1)  { // if it's blank
9296             if(this.allowBlank){
9297                 return true;
9298             }
9299             return false;
9300         }
9301         
9302         if(value.length < this.minLength){
9303             return false;
9304         }
9305         if(value.length > this.maxLength){
9306             return false;
9307         }
9308         if(this.vtype){
9309             var vt = Roo.form.VTypes;
9310             if(!vt[this.vtype](value, this)){
9311                 return false;
9312             }
9313         }
9314         if(typeof this.validator == "function"){
9315             var msg = this.validator(value);
9316             if(msg !== true){
9317                 return false;
9318             }
9319             if (typeof(msg) == 'string') {
9320                 this.invalidText = msg;
9321             }
9322         }
9323         
9324         if(this.regex && !this.regex.test(value)){
9325             return false;
9326         }
9327         
9328         return true;
9329     },
9330     
9331      // private
9332     fireKey : function(e){
9333         //Roo.log('field ' + e.getKey());
9334         if(e.isNavKeyPress()){
9335             this.fireEvent("specialkey", this, e);
9336         }
9337     },
9338     focus : function (selectText){
9339         if(this.rendered){
9340             this.inputEl().focus();
9341             if(selectText === true){
9342                 this.inputEl().dom.select();
9343             }
9344         }
9345         return this;
9346     } ,
9347     
9348     onFocus : function(){
9349         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9350            // this.el.addClass(this.focusClass);
9351         }
9352         if(!this.hasFocus){
9353             this.hasFocus = true;
9354             this.startValue = this.getValue();
9355             this.fireEvent("focus", this);
9356         }
9357     },
9358     
9359     beforeBlur : Roo.emptyFn,
9360
9361     
9362     // private
9363     onBlur : function(){
9364         this.beforeBlur();
9365         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9366             //this.el.removeClass(this.focusClass);
9367         }
9368         this.hasFocus = false;
9369         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9370             this.validate();
9371         }
9372         var v = this.getValue();
9373         if(String(v) !== String(this.startValue)){
9374             this.fireEvent('change', this, v, this.startValue);
9375         }
9376         this.fireEvent("blur", this);
9377     },
9378     
9379     onChange : function(e)
9380     {
9381         var v = this.getValue();
9382         if(String(v) !== String(this.startValue)){
9383             this.fireEvent('change', this, v, this.startValue);
9384         }
9385         
9386     },
9387     
9388     /**
9389      * Resets the current field value to the originally loaded value and clears any validation messages
9390      */
9391     reset : function(){
9392         this.setValue(this.originalValue);
9393         this.validate();
9394     },
9395      /**
9396      * Returns the name of the field
9397      * @return {Mixed} name The name field
9398      */
9399     getName: function(){
9400         return this.name;
9401     },
9402      /**
9403      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9404      * @return {Mixed} value The field value
9405      */
9406     getValue : function(){
9407         
9408         var v = this.inputEl().getValue();
9409         
9410         return v;
9411     },
9412     /**
9413      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9414      * @return {Mixed} value The field value
9415      */
9416     getRawValue : function(){
9417         var v = this.inputEl().getValue();
9418         
9419         return v;
9420     },
9421     
9422     /**
9423      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9424      * @param {Mixed} value The value to set
9425      */
9426     setRawValue : function(v){
9427         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9428     },
9429     
9430     selectText : function(start, end){
9431         var v = this.getRawValue();
9432         if(v.length > 0){
9433             start = start === undefined ? 0 : start;
9434             end = end === undefined ? v.length : end;
9435             var d = this.inputEl().dom;
9436             if(d.setSelectionRange){
9437                 d.setSelectionRange(start, end);
9438             }else if(d.createTextRange){
9439                 var range = d.createTextRange();
9440                 range.moveStart("character", start);
9441                 range.moveEnd("character", v.length-end);
9442                 range.select();
9443             }
9444         }
9445     },
9446     
9447     /**
9448      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9449      * @param {Mixed} value The value to set
9450      */
9451     setValue : function(v){
9452         this.value = v;
9453         if(this.rendered){
9454             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9455             this.validate();
9456         }
9457     },
9458     
9459     /*
9460     processValue : function(value){
9461         if(this.stripCharsRe){
9462             var newValue = value.replace(this.stripCharsRe, '');
9463             if(newValue !== value){
9464                 this.setRawValue(newValue);
9465                 return newValue;
9466             }
9467         }
9468         return value;
9469     },
9470   */
9471     preFocus : function(){
9472         
9473         if(this.selectOnFocus){
9474             this.inputEl().dom.select();
9475         }
9476     },
9477     filterKeys : function(e){
9478         var k = e.getKey();
9479         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9480             return;
9481         }
9482         var c = e.getCharCode(), cc = String.fromCharCode(c);
9483         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9484             return;
9485         }
9486         if(!this.maskRe.test(cc)){
9487             e.stopEvent();
9488         }
9489     },
9490      /**
9491      * Clear any invalid styles/messages for this field
9492      */
9493     clearInvalid : function(){
9494         
9495         if(!this.el || this.preventMark){ // not rendered
9496             return;
9497         }
9498         
9499      
9500         this.el.removeClass(this.invalidClass);
9501         
9502         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9503             
9504             var feedback = this.el.select('.form-control-feedback', true).first();
9505             
9506             if(feedback){
9507                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9508             }
9509             
9510         }
9511         
9512         if(this.indicator){
9513             this.indicator.removeClass('visible');
9514             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9515         }
9516         
9517         this.fireEvent('valid', this);
9518     },
9519     
9520      /**
9521      * Mark this field as valid
9522      */
9523     markValid : function()
9524     {
9525         if(!this.el  || this.preventMark){ // not rendered...
9526             return;
9527         }
9528         
9529         this.el.removeClass([this.invalidClass, this.validClass]);
9530         
9531         var feedback = this.el.select('.form-control-feedback', true).first();
9532             
9533         if(feedback){
9534             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9535         }
9536         
9537         if(this.indicator){
9538             this.indicator.removeClass('visible');
9539             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9540         }
9541         
9542         if(this.disabled){
9543             return;
9544         }
9545         
9546         if(this.allowBlank && !this.getRawValue().length){
9547             return;
9548         }
9549         
9550         this.el.addClass(this.validClass);
9551         
9552         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9553             
9554             var feedback = this.el.select('.form-control-feedback', true).first();
9555             
9556             if(feedback){
9557                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9558                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9559             }
9560             
9561         }
9562         
9563         this.fireEvent('valid', this);
9564     },
9565     
9566      /**
9567      * Mark this field as invalid
9568      * @param {String} msg The validation message
9569      */
9570     markInvalid : function(msg)
9571     {
9572         if(!this.el  || this.preventMark){ // not rendered
9573             return;
9574         }
9575         
9576         this.el.removeClass([this.invalidClass, this.validClass]);
9577         
9578         var feedback = this.el.select('.form-control-feedback', true).first();
9579             
9580         if(feedback){
9581             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9582         }
9583
9584         if(this.disabled){
9585             return;
9586         }
9587         
9588         if(this.allowBlank && !this.getRawValue().length){
9589             return;
9590         }
9591         
9592         if(this.indicator){
9593             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9594             this.indicator.addClass('visible');
9595         }
9596         
9597         this.el.addClass(this.invalidClass);
9598         
9599         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9600             
9601             var feedback = this.el.select('.form-control-feedback', true).first();
9602             
9603             if(feedback){
9604                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9605                 
9606                 if(this.getValue().length || this.forceFeedback){
9607                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9608                 }
9609                 
9610             }
9611             
9612         }
9613         
9614         this.fireEvent('invalid', this, msg);
9615     },
9616     // private
9617     SafariOnKeyDown : function(event)
9618     {
9619         // this is a workaround for a password hang bug on chrome/ webkit.
9620         if (this.inputEl().dom.type != 'password') {
9621             return;
9622         }
9623         
9624         var isSelectAll = false;
9625         
9626         if(this.inputEl().dom.selectionEnd > 0){
9627             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9628         }
9629         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9630             event.preventDefault();
9631             this.setValue('');
9632             return;
9633         }
9634         
9635         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9636             
9637             event.preventDefault();
9638             // this is very hacky as keydown always get's upper case.
9639             //
9640             var cc = String.fromCharCode(event.getCharCode());
9641             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9642             
9643         }
9644     },
9645     adjustWidth : function(tag, w){
9646         tag = tag.toLowerCase();
9647         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9648             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9649                 if(tag == 'input'){
9650                     return w + 2;
9651                 }
9652                 if(tag == 'textarea'){
9653                     return w-2;
9654                 }
9655             }else if(Roo.isOpera){
9656                 if(tag == 'input'){
9657                     return w + 2;
9658                 }
9659                 if(tag == 'textarea'){
9660                     return w-2;
9661                 }
9662             }
9663         }
9664         return w;
9665     },
9666     
9667     setFieldLabel : function(v)
9668     {
9669         if(!this.rendered){
9670             return;
9671         }
9672         
9673         if(this.indicator){
9674             var ar = this.el.select('label > span',true);
9675             
9676             if (ar.elements.length) {
9677                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9678                 this.fieldLabel = v;
9679                 return;
9680             }
9681             
9682             var br = this.el.select('label',true);
9683             
9684             if(br.elements.length) {
9685                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9686                 this.fieldLabel = v;
9687                 return;
9688             }
9689             
9690             Roo.log('Cannot Found any of label > span || label in input');
9691             return;
9692         }
9693         
9694         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9695         this.fieldLabel = v;
9696         
9697         
9698     }
9699 });
9700
9701  
9702 /*
9703  * - LGPL
9704  *
9705  * Input
9706  * 
9707  */
9708
9709 /**
9710  * @class Roo.bootstrap.TextArea
9711  * @extends Roo.bootstrap.Input
9712  * Bootstrap TextArea class
9713  * @cfg {Number} cols Specifies the visible width of a text area
9714  * @cfg {Number} rows Specifies the visible number of lines in a text area
9715  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9716  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9717  * @cfg {string} html text
9718  * 
9719  * @constructor
9720  * Create a new TextArea
9721  * @param {Object} config The config object
9722  */
9723
9724 Roo.bootstrap.TextArea = function(config){
9725     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9726    
9727 };
9728
9729 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9730      
9731     cols : false,
9732     rows : 5,
9733     readOnly : false,
9734     warp : 'soft',
9735     resize : false,
9736     value: false,
9737     html: false,
9738     
9739     getAutoCreate : function(){
9740         
9741         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9742         
9743         var id = Roo.id();
9744         
9745         var cfg = {};
9746         
9747         if(this.inputType != 'hidden'){
9748             cfg.cls = 'form-group' //input-group
9749         }
9750         
9751         var input =  {
9752             tag: 'textarea',
9753             id : id,
9754             warp : this.warp,
9755             rows : this.rows,
9756             value : this.value || '',
9757             html: this.html || '',
9758             cls : 'form-control',
9759             placeholder : this.placeholder || '' 
9760             
9761         };
9762         
9763         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9764             input.maxLength = this.maxLength;
9765         }
9766         
9767         if(this.resize){
9768             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9769         }
9770         
9771         if(this.cols){
9772             input.cols = this.cols;
9773         }
9774         
9775         if (this.readOnly) {
9776             input.readonly = true;
9777         }
9778         
9779         if (this.name) {
9780             input.name = this.name;
9781         }
9782         
9783         if (this.size) {
9784             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9785         }
9786         
9787         var settings=this;
9788         ['xs','sm','md','lg'].map(function(size){
9789             if (settings[size]) {
9790                 cfg.cls += ' col-' + size + '-' + settings[size];
9791             }
9792         });
9793         
9794         var inputblock = input;
9795         
9796         if(this.hasFeedback && !this.allowBlank){
9797             
9798             var feedback = {
9799                 tag: 'span',
9800                 cls: 'glyphicon form-control-feedback'
9801             };
9802
9803             inputblock = {
9804                 cls : 'has-feedback',
9805                 cn :  [
9806                     input,
9807                     feedback
9808                 ] 
9809             };  
9810         }
9811         
9812         
9813         if (this.before || this.after) {
9814             
9815             inputblock = {
9816                 cls : 'input-group',
9817                 cn :  [] 
9818             };
9819             if (this.before) {
9820                 inputblock.cn.push({
9821                     tag :'span',
9822                     cls : 'input-group-addon',
9823                     html : this.before
9824                 });
9825             }
9826             
9827             inputblock.cn.push(input);
9828             
9829             if(this.hasFeedback && !this.allowBlank){
9830                 inputblock.cls += ' has-feedback';
9831                 inputblock.cn.push(feedback);
9832             }
9833             
9834             if (this.after) {
9835                 inputblock.cn.push({
9836                     tag :'span',
9837                     cls : 'input-group-addon',
9838                     html : this.after
9839                 });
9840             }
9841             
9842         }
9843         
9844         if (align ==='left' && this.fieldLabel.length) {
9845             cfg.cn = [
9846                 {
9847                     tag: 'label',
9848                     'for' :  id,
9849                     cls : 'control-label',
9850                     html : this.fieldLabel
9851                 },
9852                 {
9853                     cls : "",
9854                     cn: [
9855                         inputblock
9856                     ]
9857                 }
9858
9859             ];
9860             
9861             if(this.labelWidth > 12){
9862                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9863             }
9864
9865             if(this.labelWidth < 13 && this.labelmd == 0){
9866                 this.labelmd = this.labelWidth;
9867             }
9868
9869             if(this.labellg > 0){
9870                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9871                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9872             }
9873
9874             if(this.labelmd > 0){
9875                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9876                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9877             }
9878
9879             if(this.labelsm > 0){
9880                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9881                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9882             }
9883
9884             if(this.labelxs > 0){
9885                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9886                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9887             }
9888             
9889         } else if ( this.fieldLabel.length) {
9890             cfg.cn = [
9891
9892                {
9893                    tag: 'label',
9894                    //cls : 'input-group-addon',
9895                    html : this.fieldLabel
9896
9897                },
9898
9899                inputblock
9900
9901            ];
9902
9903         } else {
9904
9905             cfg.cn = [
9906
9907                 inputblock
9908
9909             ];
9910                 
9911         }
9912         
9913         if (this.disabled) {
9914             input.disabled=true;
9915         }
9916         
9917         return cfg;
9918         
9919     },
9920     /**
9921      * return the real textarea element.
9922      */
9923     inputEl: function ()
9924     {
9925         return this.el.select('textarea.form-control',true).first();
9926     },
9927     
9928     /**
9929      * Clear any invalid styles/messages for this field
9930      */
9931     clearInvalid : function()
9932     {
9933         
9934         if(!this.el || this.preventMark){ // not rendered
9935             return;
9936         }
9937         
9938         var label = this.el.select('label', true).first();
9939         var icon = this.el.select('i.fa-star', true).first();
9940         
9941         if(label && icon){
9942             icon.remove();
9943         }
9944         
9945         this.el.removeClass(this.invalidClass);
9946         
9947         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9948             
9949             var feedback = this.el.select('.form-control-feedback', true).first();
9950             
9951             if(feedback){
9952                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9953             }
9954             
9955         }
9956         
9957         this.fireEvent('valid', this);
9958     },
9959     
9960      /**
9961      * Mark this field as valid
9962      */
9963     markValid : function()
9964     {
9965         if(!this.el  || this.preventMark){ // not rendered
9966             return;
9967         }
9968         
9969         this.el.removeClass([this.invalidClass, this.validClass]);
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, this.validFeedbackClass]);
9975         }
9976
9977         if(this.disabled || this.allowBlank){
9978             return;
9979         }
9980         
9981         var label = this.el.select('label', true).first();
9982         var icon = this.el.select('i.fa-star', true).first();
9983         
9984         if(label && icon){
9985             icon.remove();
9986         }
9987         
9988         this.el.addClass(this.validClass);
9989         
9990         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9991             
9992             var feedback = this.el.select('.form-control-feedback', true).first();
9993             
9994             if(feedback){
9995                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9996                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9997             }
9998             
9999         }
10000         
10001         this.fireEvent('valid', this);
10002     },
10003     
10004      /**
10005      * Mark this field as invalid
10006      * @param {String} msg The validation message
10007      */
10008     markInvalid : function(msg)
10009     {
10010         if(!this.el  || this.preventMark){ // not rendered
10011             return;
10012         }
10013         
10014         this.el.removeClass([this.invalidClass, this.validClass]);
10015         
10016         var feedback = this.el.select('.form-control-feedback', true).first();
10017             
10018         if(feedback){
10019             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10020         }
10021
10022         if(this.disabled || this.allowBlank){
10023             return;
10024         }
10025         
10026         var label = this.el.select('label', true).first();
10027         var icon = this.el.select('i.fa-star', true).first();
10028         
10029         if(!this.getValue().length && label && !icon){
10030             this.el.createChild({
10031                 tag : 'i',
10032                 cls : 'text-danger fa fa-lg fa-star',
10033                 tooltip : 'This field is required',
10034                 style : 'margin-right:5px;'
10035             }, label, true);
10036         }
10037
10038         this.el.addClass(this.invalidClass);
10039         
10040         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10041             
10042             var feedback = this.el.select('.form-control-feedback', true).first();
10043             
10044             if(feedback){
10045                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10046                 
10047                 if(this.getValue().length || this.forceFeedback){
10048                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10049                 }
10050                 
10051             }
10052             
10053         }
10054         
10055         this.fireEvent('invalid', this, msg);
10056     }
10057 });
10058
10059  
10060 /*
10061  * - LGPL
10062  *
10063  * trigger field - base class for combo..
10064  * 
10065  */
10066  
10067 /**
10068  * @class Roo.bootstrap.TriggerField
10069  * @extends Roo.bootstrap.Input
10070  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10071  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10072  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10073  * for which you can provide a custom implementation.  For example:
10074  * <pre><code>
10075 var trigger = new Roo.bootstrap.TriggerField();
10076 trigger.onTriggerClick = myTriggerFn;
10077 trigger.applyTo('my-field');
10078 </code></pre>
10079  *
10080  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10081  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10082  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10083  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10084  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10085
10086  * @constructor
10087  * Create a new TriggerField.
10088  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10089  * to the base TextField)
10090  */
10091 Roo.bootstrap.TriggerField = function(config){
10092     this.mimicing = false;
10093     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10094 };
10095
10096 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10097     /**
10098      * @cfg {String} triggerClass A CSS class to apply to the trigger
10099      */
10100      /**
10101      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10102      */
10103     hideTrigger:false,
10104
10105     /**
10106      * @cfg {Boolean} removable (true|false) special filter default false
10107      */
10108     removable : false,
10109     
10110     /** @cfg {Boolean} grow @hide */
10111     /** @cfg {Number} growMin @hide */
10112     /** @cfg {Number} growMax @hide */
10113
10114     /**
10115      * @hide 
10116      * @method
10117      */
10118     autoSize: Roo.emptyFn,
10119     // private
10120     monitorTab : true,
10121     // private
10122     deferHeight : true,
10123
10124     
10125     actionMode : 'wrap',
10126     
10127     caret : false,
10128     
10129     
10130     getAutoCreate : function(){
10131        
10132         var align = this.labelAlign || this.parentLabelAlign();
10133         
10134         var id = Roo.id();
10135         
10136         var cfg = {
10137             cls: 'form-group' //input-group
10138         };
10139         
10140         
10141         var input =  {
10142             tag: 'input',
10143             id : id,
10144             type : this.inputType,
10145             cls : 'form-control',
10146             autocomplete: 'new-password',
10147             placeholder : this.placeholder || '' 
10148             
10149         };
10150         if (this.name) {
10151             input.name = this.name;
10152         }
10153         if (this.size) {
10154             input.cls += ' input-' + this.size;
10155         }
10156         
10157         if (this.disabled) {
10158             input.disabled=true;
10159         }
10160         
10161         var inputblock = input;
10162         
10163         if(this.hasFeedback && !this.allowBlank){
10164             
10165             var feedback = {
10166                 tag: 'span',
10167                 cls: 'glyphicon form-control-feedback'
10168             };
10169             
10170             if(this.removable && !this.editable && !this.tickable){
10171                 inputblock = {
10172                     cls : 'has-feedback',
10173                     cn :  [
10174                         inputblock,
10175                         {
10176                             tag: 'button',
10177                             html : 'x',
10178                             cls : 'roo-combo-removable-btn close'
10179                         },
10180                         feedback
10181                     ] 
10182                 };
10183             } else {
10184                 inputblock = {
10185                     cls : 'has-feedback',
10186                     cn :  [
10187                         inputblock,
10188                         feedback
10189                     ] 
10190                 };
10191             }
10192
10193         } else {
10194             if(this.removable && !this.editable && !this.tickable){
10195                 inputblock = {
10196                     cls : 'roo-removable',
10197                     cn :  [
10198                         inputblock,
10199                         {
10200                             tag: 'button',
10201                             html : 'x',
10202                             cls : 'roo-combo-removable-btn close'
10203                         }
10204                     ] 
10205                 };
10206             }
10207         }
10208         
10209         if (this.before || this.after) {
10210             
10211             inputblock = {
10212                 cls : 'input-group',
10213                 cn :  [] 
10214             };
10215             if (this.before) {
10216                 inputblock.cn.push({
10217                     tag :'span',
10218                     cls : 'input-group-addon',
10219                     html : this.before
10220                 });
10221             }
10222             
10223             inputblock.cn.push(input);
10224             
10225             if(this.hasFeedback && !this.allowBlank){
10226                 inputblock.cls += ' has-feedback';
10227                 inputblock.cn.push(feedback);
10228             }
10229             
10230             if (this.after) {
10231                 inputblock.cn.push({
10232                     tag :'span',
10233                     cls : 'input-group-addon',
10234                     html : this.after
10235                 });
10236             }
10237             
10238         };
10239         
10240         var box = {
10241             tag: 'div',
10242             cn: [
10243                 {
10244                     tag: 'input',
10245                     type : 'hidden',
10246                     cls: 'form-hidden-field'
10247                 },
10248                 inputblock
10249             ]
10250             
10251         };
10252         
10253         if(this.multiple){
10254             box = {
10255                 tag: 'div',
10256                 cn: [
10257                     {
10258                         tag: 'input',
10259                         type : 'hidden',
10260                         cls: 'form-hidden-field'
10261                     },
10262                     {
10263                         tag: 'ul',
10264                         cls: 'roo-select2-choices',
10265                         cn:[
10266                             {
10267                                 tag: 'li',
10268                                 cls: 'roo-select2-search-field',
10269                                 cn: [
10270
10271                                     inputblock
10272                                 ]
10273                             }
10274                         ]
10275                     }
10276                 ]
10277             }
10278         };
10279         
10280         var combobox = {
10281             cls: 'roo-select2-container input-group',
10282             cn: [
10283                 box
10284 //                {
10285 //                    tag: 'ul',
10286 //                    cls: 'typeahead typeahead-long dropdown-menu',
10287 //                    style: 'display:none'
10288 //                }
10289             ]
10290         };
10291         
10292         if(!this.multiple && this.showToggleBtn){
10293             
10294             var caret = {
10295                         tag: 'span',
10296                         cls: 'caret'
10297              };
10298             if (this.caret != false) {
10299                 caret = {
10300                      tag: 'i',
10301                      cls: 'fa fa-' + this.caret
10302                 };
10303                 
10304             }
10305             
10306             combobox.cn.push({
10307                 tag :'span',
10308                 cls : 'input-group-addon btn dropdown-toggle',
10309                 cn : [
10310                     caret,
10311                     {
10312                         tag: 'span',
10313                         cls: 'combobox-clear',
10314                         cn  : [
10315                             {
10316                                 tag : 'i',
10317                                 cls: 'icon-remove'
10318                             }
10319                         ]
10320                     }
10321                 ]
10322
10323             })
10324         }
10325         
10326         if(this.multiple){
10327             combobox.cls += ' roo-select2-container-multi';
10328         }
10329         
10330         if (align ==='left' && this.fieldLabel.length) {
10331             
10332             cfg.cls += ' roo-form-group-label-left';
10333
10334             cfg.cn = [
10335                 {
10336                     tag : 'i',
10337                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10338                     tooltip : 'This field is required'
10339                 },
10340                 {
10341                     tag: 'label',
10342                     'for' :  id,
10343                     cls : 'control-label',
10344                     html : this.fieldLabel
10345
10346                 },
10347                 {
10348                     cls : "", 
10349                     cn: [
10350                         combobox
10351                     ]
10352                 }
10353
10354             ];
10355             
10356             var labelCfg = cfg.cn[1];
10357             var contentCfg = cfg.cn[2];
10358             
10359             if(this.indicatorpos == 'right'){
10360                 cfg.cn = [
10361                     {
10362                         tag: 'label',
10363                         'for' :  id,
10364                         cls : 'control-label',
10365                         cn : [
10366                             {
10367                                 tag : 'span',
10368                                 html : this.fieldLabel
10369                             },
10370                             {
10371                                 tag : 'i',
10372                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10373                                 tooltip : 'This field is required'
10374                             }
10375                         ]
10376                     },
10377                     {
10378                         cls : "", 
10379                         cn: [
10380                             combobox
10381                         ]
10382                     }
10383
10384                 ];
10385                 
10386                 labelCfg = cfg.cn[0];
10387                 contentCfg = cfg.cn[1];
10388             }
10389             
10390             if(this.labelWidth > 12){
10391                 labelCfg.style = "width: " + this.labelWidth + 'px';
10392             }
10393             
10394             if(this.labelWidth < 13 && this.labelmd == 0){
10395                 this.labelmd = this.labelWidth;
10396             }
10397             
10398             if(this.labellg > 0){
10399                 labelCfg.cls += ' col-lg-' + this.labellg;
10400                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10401             }
10402             
10403             if(this.labelmd > 0){
10404                 labelCfg.cls += ' col-md-' + this.labelmd;
10405                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10406             }
10407             
10408             if(this.labelsm > 0){
10409                 labelCfg.cls += ' col-sm-' + this.labelsm;
10410                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10411             }
10412             
10413             if(this.labelxs > 0){
10414                 labelCfg.cls += ' col-xs-' + this.labelxs;
10415                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10416             }
10417             
10418         } else if ( this.fieldLabel.length) {
10419 //                Roo.log(" label");
10420             cfg.cn = [
10421                 {
10422                    tag : 'i',
10423                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10424                    tooltip : 'This field is required'
10425                },
10426                {
10427                    tag: 'label',
10428                    //cls : 'input-group-addon',
10429                    html : this.fieldLabel
10430
10431                },
10432
10433                combobox
10434
10435             ];
10436             
10437             if(this.indicatorpos == 'right'){
10438                 
10439                 cfg.cn = [
10440                     {
10441                        tag: 'label',
10442                        cn : [
10443                            {
10444                                tag : 'span',
10445                                html : this.fieldLabel
10446                            },
10447                            {
10448                               tag : 'i',
10449                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10450                               tooltip : 'This field is required'
10451                            }
10452                        ]
10453
10454                     },
10455                     combobox
10456
10457                 ];
10458
10459             }
10460
10461         } else {
10462             
10463 //                Roo.log(" no label && no align");
10464                 cfg = combobox
10465                      
10466                 
10467         }
10468         
10469         var settings=this;
10470         ['xs','sm','md','lg'].map(function(size){
10471             if (settings[size]) {
10472                 cfg.cls += ' col-' + size + '-' + settings[size];
10473             }
10474         });
10475         
10476         return cfg;
10477         
10478     },
10479     
10480     
10481     
10482     // private
10483     onResize : function(w, h){
10484 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10485 //        if(typeof w == 'number'){
10486 //            var x = w - this.trigger.getWidth();
10487 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10488 //            this.trigger.setStyle('left', x+'px');
10489 //        }
10490     },
10491
10492     // private
10493     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10494
10495     // private
10496     getResizeEl : function(){
10497         return this.inputEl();
10498     },
10499
10500     // private
10501     getPositionEl : function(){
10502         return this.inputEl();
10503     },
10504
10505     // private
10506     alignErrorIcon : function(){
10507         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10508     },
10509
10510     // private
10511     initEvents : function(){
10512         
10513         this.createList();
10514         
10515         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10516         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10517         if(!this.multiple && this.showToggleBtn){
10518             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10519             if(this.hideTrigger){
10520                 this.trigger.setDisplayed(false);
10521             }
10522             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10523         }
10524         
10525         if(this.multiple){
10526             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10527         }
10528         
10529         if(this.removable && !this.editable && !this.tickable){
10530             var close = this.closeTriggerEl();
10531             
10532             if(close){
10533                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10534                 close.on('click', this.removeBtnClick, this, close);
10535             }
10536         }
10537         
10538         //this.trigger.addClassOnOver('x-form-trigger-over');
10539         //this.trigger.addClassOnClick('x-form-trigger-click');
10540         
10541         //if(!this.width){
10542         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10543         //}
10544     },
10545     
10546     closeTriggerEl : function()
10547     {
10548         var close = this.el.select('.roo-combo-removable-btn', true).first();
10549         return close ? close : false;
10550     },
10551     
10552     removeBtnClick : function(e, h, el)
10553     {
10554         e.preventDefault();
10555         
10556         if(this.fireEvent("remove", this) !== false){
10557             this.reset();
10558             this.fireEvent("afterremove", this)
10559         }
10560     },
10561     
10562     createList : function()
10563     {
10564         this.list = Roo.get(document.body).createChild({
10565             tag: 'ul',
10566             cls: 'typeahead typeahead-long dropdown-menu',
10567             style: 'display:none'
10568         });
10569         
10570         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10571         
10572     },
10573
10574     // private
10575     initTrigger : function(){
10576        
10577     },
10578
10579     // private
10580     onDestroy : function(){
10581         if(this.trigger){
10582             this.trigger.removeAllListeners();
10583           //  this.trigger.remove();
10584         }
10585         //if(this.wrap){
10586         //    this.wrap.remove();
10587         //}
10588         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10589     },
10590
10591     // private
10592     onFocus : function(){
10593         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10594         /*
10595         if(!this.mimicing){
10596             this.wrap.addClass('x-trigger-wrap-focus');
10597             this.mimicing = true;
10598             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10599             if(this.monitorTab){
10600                 this.el.on("keydown", this.checkTab, this);
10601             }
10602         }
10603         */
10604     },
10605
10606     // private
10607     checkTab : function(e){
10608         if(e.getKey() == e.TAB){
10609             this.triggerBlur();
10610         }
10611     },
10612
10613     // private
10614     onBlur : function(){
10615         // do nothing
10616     },
10617
10618     // private
10619     mimicBlur : function(e, t){
10620         /*
10621         if(!this.wrap.contains(t) && this.validateBlur()){
10622             this.triggerBlur();
10623         }
10624         */
10625     },
10626
10627     // private
10628     triggerBlur : function(){
10629         this.mimicing = false;
10630         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10631         if(this.monitorTab){
10632             this.el.un("keydown", this.checkTab, this);
10633         }
10634         //this.wrap.removeClass('x-trigger-wrap-focus');
10635         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10636     },
10637
10638     // private
10639     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10640     validateBlur : function(e, t){
10641         return true;
10642     },
10643
10644     // private
10645     onDisable : function(){
10646         this.inputEl().dom.disabled = true;
10647         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10648         //if(this.wrap){
10649         //    this.wrap.addClass('x-item-disabled');
10650         //}
10651     },
10652
10653     // private
10654     onEnable : function(){
10655         this.inputEl().dom.disabled = false;
10656         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10657         //if(this.wrap){
10658         //    this.el.removeClass('x-item-disabled');
10659         //}
10660     },
10661
10662     // private
10663     onShow : function(){
10664         var ae = this.getActionEl();
10665         
10666         if(ae){
10667             ae.dom.style.display = '';
10668             ae.dom.style.visibility = 'visible';
10669         }
10670     },
10671
10672     // private
10673     
10674     onHide : function(){
10675         var ae = this.getActionEl();
10676         ae.dom.style.display = 'none';
10677     },
10678
10679     /**
10680      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10681      * by an implementing function.
10682      * @method
10683      * @param {EventObject} e
10684      */
10685     onTriggerClick : Roo.emptyFn
10686 });
10687  /*
10688  * Based on:
10689  * Ext JS Library 1.1.1
10690  * Copyright(c) 2006-2007, Ext JS, LLC.
10691  *
10692  * Originally Released Under LGPL - original licence link has changed is not relivant.
10693  *
10694  * Fork - LGPL
10695  * <script type="text/javascript">
10696  */
10697
10698
10699 /**
10700  * @class Roo.data.SortTypes
10701  * @singleton
10702  * Defines the default sorting (casting?) comparison functions used when sorting data.
10703  */
10704 Roo.data.SortTypes = {
10705     /**
10706      * Default sort that does nothing
10707      * @param {Mixed} s The value being converted
10708      * @return {Mixed} The comparison value
10709      */
10710     none : function(s){
10711         return s;
10712     },
10713     
10714     /**
10715      * The regular expression used to strip tags
10716      * @type {RegExp}
10717      * @property
10718      */
10719     stripTagsRE : /<\/?[^>]+>/gi,
10720     
10721     /**
10722      * Strips all HTML tags to sort on text only
10723      * @param {Mixed} s The value being converted
10724      * @return {String} The comparison value
10725      */
10726     asText : function(s){
10727         return String(s).replace(this.stripTagsRE, "");
10728     },
10729     
10730     /**
10731      * Strips all HTML tags to sort on text only - Case insensitive
10732      * @param {Mixed} s The value being converted
10733      * @return {String} The comparison value
10734      */
10735     asUCText : function(s){
10736         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10737     },
10738     
10739     /**
10740      * Case insensitive string
10741      * @param {Mixed} s The value being converted
10742      * @return {String} The comparison value
10743      */
10744     asUCString : function(s) {
10745         return String(s).toUpperCase();
10746     },
10747     
10748     /**
10749      * Date sorting
10750      * @param {Mixed} s The value being converted
10751      * @return {Number} The comparison value
10752      */
10753     asDate : function(s) {
10754         if(!s){
10755             return 0;
10756         }
10757         if(s instanceof Date){
10758             return s.getTime();
10759         }
10760         return Date.parse(String(s));
10761     },
10762     
10763     /**
10764      * Float sorting
10765      * @param {Mixed} s The value being converted
10766      * @return {Float} The comparison value
10767      */
10768     asFloat : function(s) {
10769         var val = parseFloat(String(s).replace(/,/g, ""));
10770         if(isNaN(val)) {
10771             val = 0;
10772         }
10773         return val;
10774     },
10775     
10776     /**
10777      * Integer sorting
10778      * @param {Mixed} s The value being converted
10779      * @return {Number} The comparison value
10780      */
10781     asInt : function(s) {
10782         var val = parseInt(String(s).replace(/,/g, ""));
10783         if(isNaN(val)) {
10784             val = 0;
10785         }
10786         return val;
10787     }
10788 };/*
10789  * Based on:
10790  * Ext JS Library 1.1.1
10791  * Copyright(c) 2006-2007, Ext JS, LLC.
10792  *
10793  * Originally Released Under LGPL - original licence link has changed is not relivant.
10794  *
10795  * Fork - LGPL
10796  * <script type="text/javascript">
10797  */
10798
10799 /**
10800 * @class Roo.data.Record
10801  * Instances of this class encapsulate both record <em>definition</em> information, and record
10802  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10803  * to access Records cached in an {@link Roo.data.Store} object.<br>
10804  * <p>
10805  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10806  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10807  * objects.<br>
10808  * <p>
10809  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10810  * @constructor
10811  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10812  * {@link #create}. The parameters are the same.
10813  * @param {Array} data An associative Array of data values keyed by the field name.
10814  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10815  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10816  * not specified an integer id is generated.
10817  */
10818 Roo.data.Record = function(data, id){
10819     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10820     this.data = data;
10821 };
10822
10823 /**
10824  * Generate a constructor for a specific record layout.
10825  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10826  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10827  * Each field definition object may contain the following properties: <ul>
10828  * <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,
10829  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10830  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10831  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10832  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10833  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10834  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10835  * this may be omitted.</p></li>
10836  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10837  * <ul><li>auto (Default, implies no conversion)</li>
10838  * <li>string</li>
10839  * <li>int</li>
10840  * <li>float</li>
10841  * <li>boolean</li>
10842  * <li>date</li></ul></p></li>
10843  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10844  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10845  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10846  * by the Reader into an object that will be stored in the Record. It is passed the
10847  * following parameters:<ul>
10848  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10849  * </ul></p></li>
10850  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10851  * </ul>
10852  * <br>usage:<br><pre><code>
10853 var TopicRecord = Roo.data.Record.create(
10854     {name: 'title', mapping: 'topic_title'},
10855     {name: 'author', mapping: 'username'},
10856     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10857     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10858     {name: 'lastPoster', mapping: 'user2'},
10859     {name: 'excerpt', mapping: 'post_text'}
10860 );
10861
10862 var myNewRecord = new TopicRecord({
10863     title: 'Do my job please',
10864     author: 'noobie',
10865     totalPosts: 1,
10866     lastPost: new Date(),
10867     lastPoster: 'Animal',
10868     excerpt: 'No way dude!'
10869 });
10870 myStore.add(myNewRecord);
10871 </code></pre>
10872  * @method create
10873  * @static
10874  */
10875 Roo.data.Record.create = function(o){
10876     var f = function(){
10877         f.superclass.constructor.apply(this, arguments);
10878     };
10879     Roo.extend(f, Roo.data.Record);
10880     var p = f.prototype;
10881     p.fields = new Roo.util.MixedCollection(false, function(field){
10882         return field.name;
10883     });
10884     for(var i = 0, len = o.length; i < len; i++){
10885         p.fields.add(new Roo.data.Field(o[i]));
10886     }
10887     f.getField = function(name){
10888         return p.fields.get(name);  
10889     };
10890     return f;
10891 };
10892
10893 Roo.data.Record.AUTO_ID = 1000;
10894 Roo.data.Record.EDIT = 'edit';
10895 Roo.data.Record.REJECT = 'reject';
10896 Roo.data.Record.COMMIT = 'commit';
10897
10898 Roo.data.Record.prototype = {
10899     /**
10900      * Readonly flag - true if this record has been modified.
10901      * @type Boolean
10902      */
10903     dirty : false,
10904     editing : false,
10905     error: null,
10906     modified: null,
10907
10908     // private
10909     join : function(store){
10910         this.store = store;
10911     },
10912
10913     /**
10914      * Set the named field to the specified value.
10915      * @param {String} name The name of the field to set.
10916      * @param {Object} value The value to set the field to.
10917      */
10918     set : function(name, value){
10919         if(this.data[name] == value){
10920             return;
10921         }
10922         this.dirty = true;
10923         if(!this.modified){
10924             this.modified = {};
10925         }
10926         if(typeof this.modified[name] == 'undefined'){
10927             this.modified[name] = this.data[name];
10928         }
10929         this.data[name] = value;
10930         if(!this.editing && this.store){
10931             this.store.afterEdit(this);
10932         }       
10933     },
10934
10935     /**
10936      * Get the value of the named field.
10937      * @param {String} name The name of the field to get the value of.
10938      * @return {Object} The value of the field.
10939      */
10940     get : function(name){
10941         return this.data[name]; 
10942     },
10943
10944     // private
10945     beginEdit : function(){
10946         this.editing = true;
10947         this.modified = {}; 
10948     },
10949
10950     // private
10951     cancelEdit : function(){
10952         this.editing = false;
10953         delete this.modified;
10954     },
10955
10956     // private
10957     endEdit : function(){
10958         this.editing = false;
10959         if(this.dirty && this.store){
10960             this.store.afterEdit(this);
10961         }
10962     },
10963
10964     /**
10965      * Usually called by the {@link Roo.data.Store} which owns the Record.
10966      * Rejects all changes made to the Record since either creation, or the last commit operation.
10967      * Modified fields are reverted to their original values.
10968      * <p>
10969      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10970      * of reject operations.
10971      */
10972     reject : function(){
10973         var m = this.modified;
10974         for(var n in m){
10975             if(typeof m[n] != "function"){
10976                 this.data[n] = m[n];
10977             }
10978         }
10979         this.dirty = false;
10980         delete this.modified;
10981         this.editing = false;
10982         if(this.store){
10983             this.store.afterReject(this);
10984         }
10985     },
10986
10987     /**
10988      * Usually called by the {@link Roo.data.Store} which owns the Record.
10989      * Commits all changes made to the Record since either creation, or the last commit operation.
10990      * <p>
10991      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10992      * of commit operations.
10993      */
10994     commit : function(){
10995         this.dirty = false;
10996         delete this.modified;
10997         this.editing = false;
10998         if(this.store){
10999             this.store.afterCommit(this);
11000         }
11001     },
11002
11003     // private
11004     hasError : function(){
11005         return this.error != null;
11006     },
11007
11008     // private
11009     clearError : function(){
11010         this.error = null;
11011     },
11012
11013     /**
11014      * Creates a copy of this record.
11015      * @param {String} id (optional) A new record id if you don't want to use this record's id
11016      * @return {Record}
11017      */
11018     copy : function(newId) {
11019         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11020     }
11021 };/*
11022  * Based on:
11023  * Ext JS Library 1.1.1
11024  * Copyright(c) 2006-2007, Ext JS, LLC.
11025  *
11026  * Originally Released Under LGPL - original licence link has changed is not relivant.
11027  *
11028  * Fork - LGPL
11029  * <script type="text/javascript">
11030  */
11031
11032
11033
11034 /**
11035  * @class Roo.data.Store
11036  * @extends Roo.util.Observable
11037  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11038  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11039  * <p>
11040  * 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
11041  * has no knowledge of the format of the data returned by the Proxy.<br>
11042  * <p>
11043  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11044  * instances from the data object. These records are cached and made available through accessor functions.
11045  * @constructor
11046  * Creates a new Store.
11047  * @param {Object} config A config object containing the objects needed for the Store to access data,
11048  * and read the data into Records.
11049  */
11050 Roo.data.Store = function(config){
11051     this.data = new Roo.util.MixedCollection(false);
11052     this.data.getKey = function(o){
11053         return o.id;
11054     };
11055     this.baseParams = {};
11056     // private
11057     this.paramNames = {
11058         "start" : "start",
11059         "limit" : "limit",
11060         "sort" : "sort",
11061         "dir" : "dir",
11062         "multisort" : "_multisort"
11063     };
11064
11065     if(config && config.data){
11066         this.inlineData = config.data;
11067         delete config.data;
11068     }
11069
11070     Roo.apply(this, config);
11071     
11072     if(this.reader){ // reader passed
11073         this.reader = Roo.factory(this.reader, Roo.data);
11074         this.reader.xmodule = this.xmodule || false;
11075         if(!this.recordType){
11076             this.recordType = this.reader.recordType;
11077         }
11078         if(this.reader.onMetaChange){
11079             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11080         }
11081     }
11082
11083     if(this.recordType){
11084         this.fields = this.recordType.prototype.fields;
11085     }
11086     this.modified = [];
11087
11088     this.addEvents({
11089         /**
11090          * @event datachanged
11091          * Fires when the data cache has changed, and a widget which is using this Store
11092          * as a Record cache should refresh its view.
11093          * @param {Store} this
11094          */
11095         datachanged : true,
11096         /**
11097          * @event metachange
11098          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11099          * @param {Store} this
11100          * @param {Object} meta The JSON metadata
11101          */
11102         metachange : true,
11103         /**
11104          * @event add
11105          * Fires when Records have been added to the Store
11106          * @param {Store} this
11107          * @param {Roo.data.Record[]} records The array of Records added
11108          * @param {Number} index The index at which the record(s) were added
11109          */
11110         add : true,
11111         /**
11112          * @event remove
11113          * Fires when a Record has been removed from the Store
11114          * @param {Store} this
11115          * @param {Roo.data.Record} record The Record that was removed
11116          * @param {Number} index The index at which the record was removed
11117          */
11118         remove : true,
11119         /**
11120          * @event update
11121          * Fires when a Record has been updated
11122          * @param {Store} this
11123          * @param {Roo.data.Record} record The Record that was updated
11124          * @param {String} operation The update operation being performed.  Value may be one of:
11125          * <pre><code>
11126  Roo.data.Record.EDIT
11127  Roo.data.Record.REJECT
11128  Roo.data.Record.COMMIT
11129          * </code></pre>
11130          */
11131         update : true,
11132         /**
11133          * @event clear
11134          * Fires when the data cache has been cleared.
11135          * @param {Store} this
11136          */
11137         clear : true,
11138         /**
11139          * @event beforeload
11140          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11141          * the load action will be canceled.
11142          * @param {Store} this
11143          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11144          */
11145         beforeload : true,
11146         /**
11147          * @event beforeloadadd
11148          * Fires after a new set of Records has been loaded.
11149          * @param {Store} this
11150          * @param {Roo.data.Record[]} records The Records that were loaded
11151          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11152          */
11153         beforeloadadd : true,
11154         /**
11155          * @event load
11156          * Fires after a new set of Records has been loaded, before they are added to the store.
11157          * @param {Store} this
11158          * @param {Roo.data.Record[]} records The Records that were loaded
11159          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11160          * @params {Object} return from reader
11161          */
11162         load : true,
11163         /**
11164          * @event loadexception
11165          * Fires if an exception occurs in the Proxy during loading.
11166          * Called with the signature of the Proxy's "loadexception" event.
11167          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11168          * 
11169          * @param {Proxy} 
11170          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11171          * @param {Object} load options 
11172          * @param {Object} jsonData from your request (normally this contains the Exception)
11173          */
11174         loadexception : true
11175     });
11176     
11177     if(this.proxy){
11178         this.proxy = Roo.factory(this.proxy, Roo.data);
11179         this.proxy.xmodule = this.xmodule || false;
11180         this.relayEvents(this.proxy,  ["loadexception"]);
11181     }
11182     this.sortToggle = {};
11183     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11184
11185     Roo.data.Store.superclass.constructor.call(this);
11186
11187     if(this.inlineData){
11188         this.loadData(this.inlineData);
11189         delete this.inlineData;
11190     }
11191 };
11192
11193 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11194      /**
11195     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11196     * without a remote query - used by combo/forms at present.
11197     */
11198     
11199     /**
11200     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11201     */
11202     /**
11203     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11204     */
11205     /**
11206     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11207     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11208     */
11209     /**
11210     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11211     * on any HTTP request
11212     */
11213     /**
11214     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11215     */
11216     /**
11217     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11218     */
11219     multiSort: false,
11220     /**
11221     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11222     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11223     */
11224     remoteSort : false,
11225
11226     /**
11227     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11228      * loaded or when a record is removed. (defaults to false).
11229     */
11230     pruneModifiedRecords : false,
11231
11232     // private
11233     lastOptions : null,
11234
11235     /**
11236      * Add Records to the Store and fires the add event.
11237      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11238      */
11239     add : function(records){
11240         records = [].concat(records);
11241         for(var i = 0, len = records.length; i < len; i++){
11242             records[i].join(this);
11243         }
11244         var index = this.data.length;
11245         this.data.addAll(records);
11246         this.fireEvent("add", this, records, index);
11247     },
11248
11249     /**
11250      * Remove a Record from the Store and fires the remove event.
11251      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11252      */
11253     remove : function(record){
11254         var index = this.data.indexOf(record);
11255         this.data.removeAt(index);
11256  
11257         if(this.pruneModifiedRecords){
11258             this.modified.remove(record);
11259         }
11260         this.fireEvent("remove", this, record, index);
11261     },
11262
11263     /**
11264      * Remove all Records from the Store and fires the clear event.
11265      */
11266     removeAll : function(){
11267         this.data.clear();
11268         if(this.pruneModifiedRecords){
11269             this.modified = [];
11270         }
11271         this.fireEvent("clear", this);
11272     },
11273
11274     /**
11275      * Inserts Records to the Store at the given index and fires the add event.
11276      * @param {Number} index The start index at which to insert the passed Records.
11277      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11278      */
11279     insert : function(index, records){
11280         records = [].concat(records);
11281         for(var i = 0, len = records.length; i < len; i++){
11282             this.data.insert(index, records[i]);
11283             records[i].join(this);
11284         }
11285         this.fireEvent("add", this, records, index);
11286     },
11287
11288     /**
11289      * Get the index within the cache of the passed Record.
11290      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11291      * @return {Number} The index of the passed Record. Returns -1 if not found.
11292      */
11293     indexOf : function(record){
11294         return this.data.indexOf(record);
11295     },
11296
11297     /**
11298      * Get the index within the cache of the Record with the passed id.
11299      * @param {String} id The id of the Record to find.
11300      * @return {Number} The index of the Record. Returns -1 if not found.
11301      */
11302     indexOfId : function(id){
11303         return this.data.indexOfKey(id);
11304     },
11305
11306     /**
11307      * Get the Record with the specified id.
11308      * @param {String} id The id of the Record to find.
11309      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11310      */
11311     getById : function(id){
11312         return this.data.key(id);
11313     },
11314
11315     /**
11316      * Get the Record at the specified index.
11317      * @param {Number} index The index of the Record to find.
11318      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11319      */
11320     getAt : function(index){
11321         return this.data.itemAt(index);
11322     },
11323
11324     /**
11325      * Returns a range of Records between specified indices.
11326      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11327      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11328      * @return {Roo.data.Record[]} An array of Records
11329      */
11330     getRange : function(start, end){
11331         return this.data.getRange(start, end);
11332     },
11333
11334     // private
11335     storeOptions : function(o){
11336         o = Roo.apply({}, o);
11337         delete o.callback;
11338         delete o.scope;
11339         this.lastOptions = o;
11340     },
11341
11342     /**
11343      * Loads the Record cache from the configured Proxy using the configured Reader.
11344      * <p>
11345      * If using remote paging, then the first load call must specify the <em>start</em>
11346      * and <em>limit</em> properties in the options.params property to establish the initial
11347      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11348      * <p>
11349      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11350      * and this call will return before the new data has been loaded. Perform any post-processing
11351      * in a callback function, or in a "load" event handler.</strong>
11352      * <p>
11353      * @param {Object} options An object containing properties which control loading options:<ul>
11354      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11355      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11356      * passed the following arguments:<ul>
11357      * <li>r : Roo.data.Record[]</li>
11358      * <li>options: Options object from the load call</li>
11359      * <li>success: Boolean success indicator</li></ul></li>
11360      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11361      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11362      * </ul>
11363      */
11364     load : function(options){
11365         options = options || {};
11366         if(this.fireEvent("beforeload", this, options) !== false){
11367             this.storeOptions(options);
11368             var p = Roo.apply(options.params || {}, this.baseParams);
11369             // if meta was not loaded from remote source.. try requesting it.
11370             if (!this.reader.metaFromRemote) {
11371                 p._requestMeta = 1;
11372             }
11373             if(this.sortInfo && this.remoteSort){
11374                 var pn = this.paramNames;
11375                 p[pn["sort"]] = this.sortInfo.field;
11376                 p[pn["dir"]] = this.sortInfo.direction;
11377             }
11378             if (this.multiSort) {
11379                 var pn = this.paramNames;
11380                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11381             }
11382             
11383             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11384         }
11385     },
11386
11387     /**
11388      * Reloads the Record cache from the configured Proxy using the configured Reader and
11389      * the options from the last load operation performed.
11390      * @param {Object} options (optional) An object containing properties which may override the options
11391      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11392      * the most recently used options are reused).
11393      */
11394     reload : function(options){
11395         this.load(Roo.applyIf(options||{}, this.lastOptions));
11396     },
11397
11398     // private
11399     // Called as a callback by the Reader during a load operation.
11400     loadRecords : function(o, options, success){
11401         if(!o || success === false){
11402             if(success !== false){
11403                 this.fireEvent("load", this, [], options, o);
11404             }
11405             if(options.callback){
11406                 options.callback.call(options.scope || this, [], options, false);
11407             }
11408             return;
11409         }
11410         // if data returned failure - throw an exception.
11411         if (o.success === false) {
11412             // show a message if no listener is registered.
11413             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11414                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11415             }
11416             // loadmask wil be hooked into this..
11417             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11418             return;
11419         }
11420         var r = o.records, t = o.totalRecords || r.length;
11421         
11422         this.fireEvent("beforeloadadd", this, r, options, o);
11423         
11424         if(!options || options.add !== true){
11425             if(this.pruneModifiedRecords){
11426                 this.modified = [];
11427             }
11428             for(var i = 0, len = r.length; i < len; i++){
11429                 r[i].join(this);
11430             }
11431             if(this.snapshot){
11432                 this.data = this.snapshot;
11433                 delete this.snapshot;
11434             }
11435             this.data.clear();
11436             this.data.addAll(r);
11437             this.totalLength = t;
11438             this.applySort();
11439             this.fireEvent("datachanged", this);
11440         }else{
11441             this.totalLength = Math.max(t, this.data.length+r.length);
11442             this.add(r);
11443         }
11444         
11445         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11446                 
11447             var e = new Roo.data.Record({});
11448
11449             e.set(this.parent.displayField, this.parent.emptyTitle);
11450             e.set(this.parent.valueField, '');
11451
11452             this.insert(0, e);
11453         }
11454             
11455         this.fireEvent("load", this, r, options, o);
11456         if(options.callback){
11457             options.callback.call(options.scope || this, r, options, true);
11458         }
11459     },
11460
11461
11462     /**
11463      * Loads data from a passed data block. A Reader which understands the format of the data
11464      * must have been configured in the constructor.
11465      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11466      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11467      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11468      */
11469     loadData : function(o, append){
11470         var r = this.reader.readRecords(o);
11471         this.loadRecords(r, {add: append}, true);
11472     },
11473
11474     /**
11475      * Gets the number of cached records.
11476      * <p>
11477      * <em>If using paging, this may not be the total size of the dataset. If the data object
11478      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11479      * the data set size</em>
11480      */
11481     getCount : function(){
11482         return this.data.length || 0;
11483     },
11484
11485     /**
11486      * Gets the total number of records in the dataset as returned by the server.
11487      * <p>
11488      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11489      * the dataset size</em>
11490      */
11491     getTotalCount : function(){
11492         return this.totalLength || 0;
11493     },
11494
11495     /**
11496      * Returns the sort state of the Store as an object with two properties:
11497      * <pre><code>
11498  field {String} The name of the field by which the Records are sorted
11499  direction {String} The sort order, "ASC" or "DESC"
11500      * </code></pre>
11501      */
11502     getSortState : function(){
11503         return this.sortInfo;
11504     },
11505
11506     // private
11507     applySort : function(){
11508         if(this.sortInfo && !this.remoteSort){
11509             var s = this.sortInfo, f = s.field;
11510             var st = this.fields.get(f).sortType;
11511             var fn = function(r1, r2){
11512                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11513                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11514             };
11515             this.data.sort(s.direction, fn);
11516             if(this.snapshot && this.snapshot != this.data){
11517                 this.snapshot.sort(s.direction, fn);
11518             }
11519         }
11520     },
11521
11522     /**
11523      * Sets the default sort column and order to be used by the next load operation.
11524      * @param {String} fieldName The name of the field to sort by.
11525      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11526      */
11527     setDefaultSort : function(field, dir){
11528         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11529     },
11530
11531     /**
11532      * Sort the Records.
11533      * If remote sorting is used, the sort is performed on the server, and the cache is
11534      * reloaded. If local sorting is used, the cache is sorted internally.
11535      * @param {String} fieldName The name of the field to sort by.
11536      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11537      */
11538     sort : function(fieldName, dir){
11539         var f = this.fields.get(fieldName);
11540         if(!dir){
11541             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11542             
11543             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11544                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11545             }else{
11546                 dir = f.sortDir;
11547             }
11548         }
11549         this.sortToggle[f.name] = dir;
11550         this.sortInfo = {field: f.name, direction: dir};
11551         if(!this.remoteSort){
11552             this.applySort();
11553             this.fireEvent("datachanged", this);
11554         }else{
11555             this.load(this.lastOptions);
11556         }
11557     },
11558
11559     /**
11560      * Calls the specified function for each of the Records in the cache.
11561      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11562      * Returning <em>false</em> aborts and exits the iteration.
11563      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11564      */
11565     each : function(fn, scope){
11566         this.data.each(fn, scope);
11567     },
11568
11569     /**
11570      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11571      * (e.g., during paging).
11572      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11573      */
11574     getModifiedRecords : function(){
11575         return this.modified;
11576     },
11577
11578     // private
11579     createFilterFn : function(property, value, anyMatch){
11580         if(!value.exec){ // not a regex
11581             value = String(value);
11582             if(value.length == 0){
11583                 return false;
11584             }
11585             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11586         }
11587         return function(r){
11588             return value.test(r.data[property]);
11589         };
11590     },
11591
11592     /**
11593      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11594      * @param {String} property A field on your records
11595      * @param {Number} start The record index to start at (defaults to 0)
11596      * @param {Number} end The last record index to include (defaults to length - 1)
11597      * @return {Number} The sum
11598      */
11599     sum : function(property, start, end){
11600         var rs = this.data.items, v = 0;
11601         start = start || 0;
11602         end = (end || end === 0) ? end : rs.length-1;
11603
11604         for(var i = start; i <= end; i++){
11605             v += (rs[i].data[property] || 0);
11606         }
11607         return v;
11608     },
11609
11610     /**
11611      * Filter the records by a specified property.
11612      * @param {String} field A field on your records
11613      * @param {String/RegExp} value Either a string that the field
11614      * should start with or a RegExp to test against the field
11615      * @param {Boolean} anyMatch True to match any part not just the beginning
11616      */
11617     filter : function(property, value, anyMatch){
11618         var fn = this.createFilterFn(property, value, anyMatch);
11619         return fn ? this.filterBy(fn) : this.clearFilter();
11620     },
11621
11622     /**
11623      * Filter by a function. The specified function will be called with each
11624      * record in this data source. If the function returns true the record is included,
11625      * otherwise it is filtered.
11626      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11627      * @param {Object} scope (optional) The scope of the function (defaults to this)
11628      */
11629     filterBy : function(fn, scope){
11630         this.snapshot = this.snapshot || this.data;
11631         this.data = this.queryBy(fn, scope||this);
11632         this.fireEvent("datachanged", this);
11633     },
11634
11635     /**
11636      * Query the records by a specified property.
11637      * @param {String} field A field on your records
11638      * @param {String/RegExp} value Either a string that the field
11639      * should start with or a RegExp to test against the field
11640      * @param {Boolean} anyMatch True to match any part not just the beginning
11641      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11642      */
11643     query : function(property, value, anyMatch){
11644         var fn = this.createFilterFn(property, value, anyMatch);
11645         return fn ? this.queryBy(fn) : this.data.clone();
11646     },
11647
11648     /**
11649      * Query by a function. The specified function will be called with each
11650      * record in this data source. If the function returns true the record is included
11651      * in the results.
11652      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11653      * @param {Object} scope (optional) The scope of the function (defaults to this)
11654       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11655      **/
11656     queryBy : function(fn, scope){
11657         var data = this.snapshot || this.data;
11658         return data.filterBy(fn, scope||this);
11659     },
11660
11661     /**
11662      * Collects unique values for a particular dataIndex from this store.
11663      * @param {String} dataIndex The property to collect
11664      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11665      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11666      * @return {Array} An array of the unique values
11667      **/
11668     collect : function(dataIndex, allowNull, bypassFilter){
11669         var d = (bypassFilter === true && this.snapshot) ?
11670                 this.snapshot.items : this.data.items;
11671         var v, sv, r = [], l = {};
11672         for(var i = 0, len = d.length; i < len; i++){
11673             v = d[i].data[dataIndex];
11674             sv = String(v);
11675             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11676                 l[sv] = true;
11677                 r[r.length] = v;
11678             }
11679         }
11680         return r;
11681     },
11682
11683     /**
11684      * Revert to a view of the Record cache with no filtering applied.
11685      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11686      */
11687     clearFilter : function(suppressEvent){
11688         if(this.snapshot && this.snapshot != this.data){
11689             this.data = this.snapshot;
11690             delete this.snapshot;
11691             if(suppressEvent !== true){
11692                 this.fireEvent("datachanged", this);
11693             }
11694         }
11695     },
11696
11697     // private
11698     afterEdit : function(record){
11699         if(this.modified.indexOf(record) == -1){
11700             this.modified.push(record);
11701         }
11702         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11703     },
11704     
11705     // private
11706     afterReject : function(record){
11707         this.modified.remove(record);
11708         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11709     },
11710
11711     // private
11712     afterCommit : function(record){
11713         this.modified.remove(record);
11714         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11715     },
11716
11717     /**
11718      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11719      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11720      */
11721     commitChanges : function(){
11722         var m = this.modified.slice(0);
11723         this.modified = [];
11724         for(var i = 0, len = m.length; i < len; i++){
11725             m[i].commit();
11726         }
11727     },
11728
11729     /**
11730      * Cancel outstanding changes on all changed records.
11731      */
11732     rejectChanges : function(){
11733         var m = this.modified.slice(0);
11734         this.modified = [];
11735         for(var i = 0, len = m.length; i < len; i++){
11736             m[i].reject();
11737         }
11738     },
11739
11740     onMetaChange : function(meta, rtype, o){
11741         this.recordType = rtype;
11742         this.fields = rtype.prototype.fields;
11743         delete this.snapshot;
11744         this.sortInfo = meta.sortInfo || this.sortInfo;
11745         this.modified = [];
11746         this.fireEvent('metachange', this, this.reader.meta);
11747     },
11748     
11749     moveIndex : function(data, type)
11750     {
11751         var index = this.indexOf(data);
11752         
11753         var newIndex = index + type;
11754         
11755         this.remove(data);
11756         
11757         this.insert(newIndex, data);
11758         
11759     }
11760 });/*
11761  * Based on:
11762  * Ext JS Library 1.1.1
11763  * Copyright(c) 2006-2007, Ext JS, LLC.
11764  *
11765  * Originally Released Under LGPL - original licence link has changed is not relivant.
11766  *
11767  * Fork - LGPL
11768  * <script type="text/javascript">
11769  */
11770
11771 /**
11772  * @class Roo.data.SimpleStore
11773  * @extends Roo.data.Store
11774  * Small helper class to make creating Stores from Array data easier.
11775  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11776  * @cfg {Array} fields An array of field definition objects, or field name strings.
11777  * @cfg {Array} data The multi-dimensional array of data
11778  * @constructor
11779  * @param {Object} config
11780  */
11781 Roo.data.SimpleStore = function(config){
11782     Roo.data.SimpleStore.superclass.constructor.call(this, {
11783         isLocal : true,
11784         reader: new Roo.data.ArrayReader({
11785                 id: config.id
11786             },
11787             Roo.data.Record.create(config.fields)
11788         ),
11789         proxy : new Roo.data.MemoryProxy(config.data)
11790     });
11791     this.load();
11792 };
11793 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11794  * Based on:
11795  * Ext JS Library 1.1.1
11796  * Copyright(c) 2006-2007, Ext JS, LLC.
11797  *
11798  * Originally Released Under LGPL - original licence link has changed is not relivant.
11799  *
11800  * Fork - LGPL
11801  * <script type="text/javascript">
11802  */
11803
11804 /**
11805 /**
11806  * @extends Roo.data.Store
11807  * @class Roo.data.JsonStore
11808  * Small helper class to make creating Stores for JSON data easier. <br/>
11809 <pre><code>
11810 var store = new Roo.data.JsonStore({
11811     url: 'get-images.php',
11812     root: 'images',
11813     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11814 });
11815 </code></pre>
11816  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11817  * JsonReader and HttpProxy (unless inline data is provided).</b>
11818  * @cfg {Array} fields An array of field definition objects, or field name strings.
11819  * @constructor
11820  * @param {Object} config
11821  */
11822 Roo.data.JsonStore = function(c){
11823     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11824         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11825         reader: new Roo.data.JsonReader(c, c.fields)
11826     }));
11827 };
11828 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11829  * Based on:
11830  * Ext JS Library 1.1.1
11831  * Copyright(c) 2006-2007, Ext JS, LLC.
11832  *
11833  * Originally Released Under LGPL - original licence link has changed is not relivant.
11834  *
11835  * Fork - LGPL
11836  * <script type="text/javascript">
11837  */
11838
11839  
11840 Roo.data.Field = function(config){
11841     if(typeof config == "string"){
11842         config = {name: config};
11843     }
11844     Roo.apply(this, config);
11845     
11846     if(!this.type){
11847         this.type = "auto";
11848     }
11849     
11850     var st = Roo.data.SortTypes;
11851     // named sortTypes are supported, here we look them up
11852     if(typeof this.sortType == "string"){
11853         this.sortType = st[this.sortType];
11854     }
11855     
11856     // set default sortType for strings and dates
11857     if(!this.sortType){
11858         switch(this.type){
11859             case "string":
11860                 this.sortType = st.asUCString;
11861                 break;
11862             case "date":
11863                 this.sortType = st.asDate;
11864                 break;
11865             default:
11866                 this.sortType = st.none;
11867         }
11868     }
11869
11870     // define once
11871     var stripRe = /[\$,%]/g;
11872
11873     // prebuilt conversion function for this field, instead of
11874     // switching every time we're reading a value
11875     if(!this.convert){
11876         var cv, dateFormat = this.dateFormat;
11877         switch(this.type){
11878             case "":
11879             case "auto":
11880             case undefined:
11881                 cv = function(v){ return v; };
11882                 break;
11883             case "string":
11884                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11885                 break;
11886             case "int":
11887                 cv = function(v){
11888                     return v !== undefined && v !== null && v !== '' ?
11889                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11890                     };
11891                 break;
11892             case "float":
11893                 cv = function(v){
11894                     return v !== undefined && v !== null && v !== '' ?
11895                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11896                     };
11897                 break;
11898             case "bool":
11899             case "boolean":
11900                 cv = function(v){ return v === true || v === "true" || v == 1; };
11901                 break;
11902             case "date":
11903                 cv = function(v){
11904                     if(!v){
11905                         return '';
11906                     }
11907                     if(v instanceof Date){
11908                         return v;
11909                     }
11910                     if(dateFormat){
11911                         if(dateFormat == "timestamp"){
11912                             return new Date(v*1000);
11913                         }
11914                         return Date.parseDate(v, dateFormat);
11915                     }
11916                     var parsed = Date.parse(v);
11917                     return parsed ? new Date(parsed) : null;
11918                 };
11919              break;
11920             
11921         }
11922         this.convert = cv;
11923     }
11924 };
11925
11926 Roo.data.Field.prototype = {
11927     dateFormat: null,
11928     defaultValue: "",
11929     mapping: null,
11930     sortType : null,
11931     sortDir : "ASC"
11932 };/*
11933  * Based on:
11934  * Ext JS Library 1.1.1
11935  * Copyright(c) 2006-2007, Ext JS, LLC.
11936  *
11937  * Originally Released Under LGPL - original licence link has changed is not relivant.
11938  *
11939  * Fork - LGPL
11940  * <script type="text/javascript">
11941  */
11942  
11943 // Base class for reading structured data from a data source.  This class is intended to be
11944 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11945
11946 /**
11947  * @class Roo.data.DataReader
11948  * Base class for reading structured data from a data source.  This class is intended to be
11949  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11950  */
11951
11952 Roo.data.DataReader = function(meta, recordType){
11953     
11954     this.meta = meta;
11955     
11956     this.recordType = recordType instanceof Array ? 
11957         Roo.data.Record.create(recordType) : recordType;
11958 };
11959
11960 Roo.data.DataReader.prototype = {
11961      /**
11962      * Create an empty record
11963      * @param {Object} data (optional) - overlay some values
11964      * @return {Roo.data.Record} record created.
11965      */
11966     newRow :  function(d) {
11967         var da =  {};
11968         this.recordType.prototype.fields.each(function(c) {
11969             switch( c.type) {
11970                 case 'int' : da[c.name] = 0; break;
11971                 case 'date' : da[c.name] = new Date(); break;
11972                 case 'float' : da[c.name] = 0.0; break;
11973                 case 'boolean' : da[c.name] = false; break;
11974                 default : da[c.name] = ""; break;
11975             }
11976             
11977         });
11978         return new this.recordType(Roo.apply(da, d));
11979     }
11980     
11981 };/*
11982  * Based on:
11983  * Ext JS Library 1.1.1
11984  * Copyright(c) 2006-2007, Ext JS, LLC.
11985  *
11986  * Originally Released Under LGPL - original licence link has changed is not relivant.
11987  *
11988  * Fork - LGPL
11989  * <script type="text/javascript">
11990  */
11991
11992 /**
11993  * @class Roo.data.DataProxy
11994  * @extends Roo.data.Observable
11995  * This class is an abstract base class for implementations which provide retrieval of
11996  * unformatted data objects.<br>
11997  * <p>
11998  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11999  * (of the appropriate type which knows how to parse the data object) to provide a block of
12000  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12001  * <p>
12002  * Custom implementations must implement the load method as described in
12003  * {@link Roo.data.HttpProxy#load}.
12004  */
12005 Roo.data.DataProxy = function(){
12006     this.addEvents({
12007         /**
12008          * @event beforeload
12009          * Fires before a network request is made to retrieve a data object.
12010          * @param {Object} This DataProxy object.
12011          * @param {Object} params The params parameter to the load function.
12012          */
12013         beforeload : true,
12014         /**
12015          * @event load
12016          * Fires before the load method's callback is called.
12017          * @param {Object} This DataProxy object.
12018          * @param {Object} o The data object.
12019          * @param {Object} arg The callback argument object passed to the load function.
12020          */
12021         load : true,
12022         /**
12023          * @event loadexception
12024          * Fires if an Exception occurs during data retrieval.
12025          * @param {Object} This DataProxy object.
12026          * @param {Object} o The data object.
12027          * @param {Object} arg The callback argument object passed to the load function.
12028          * @param {Object} e The Exception.
12029          */
12030         loadexception : true
12031     });
12032     Roo.data.DataProxy.superclass.constructor.call(this);
12033 };
12034
12035 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12036
12037     /**
12038      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12039      */
12040 /*
12041  * Based on:
12042  * Ext JS Library 1.1.1
12043  * Copyright(c) 2006-2007, Ext JS, LLC.
12044  *
12045  * Originally Released Under LGPL - original licence link has changed is not relivant.
12046  *
12047  * Fork - LGPL
12048  * <script type="text/javascript">
12049  */
12050 /**
12051  * @class Roo.data.MemoryProxy
12052  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12053  * to the Reader when its load method is called.
12054  * @constructor
12055  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12056  */
12057 Roo.data.MemoryProxy = function(data){
12058     if (data.data) {
12059         data = data.data;
12060     }
12061     Roo.data.MemoryProxy.superclass.constructor.call(this);
12062     this.data = data;
12063 };
12064
12065 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12066     
12067     /**
12068      * Load data from the requested source (in this case an in-memory
12069      * data object passed to the constructor), read the data object into
12070      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12071      * process that block using the passed callback.
12072      * @param {Object} params This parameter is not used by the MemoryProxy class.
12073      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12074      * object into a block of Roo.data.Records.
12075      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12076      * The function must be passed <ul>
12077      * <li>The Record block object</li>
12078      * <li>The "arg" argument from the load function</li>
12079      * <li>A boolean success indicator</li>
12080      * </ul>
12081      * @param {Object} scope The scope in which to call the callback
12082      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12083      */
12084     load : function(params, reader, callback, scope, arg){
12085         params = params || {};
12086         var result;
12087         try {
12088             result = reader.readRecords(this.data);
12089         }catch(e){
12090             this.fireEvent("loadexception", this, arg, null, e);
12091             callback.call(scope, null, arg, false);
12092             return;
12093         }
12094         callback.call(scope, result, arg, true);
12095     },
12096     
12097     // private
12098     update : function(params, records){
12099         
12100     }
12101 });/*
12102  * Based on:
12103  * Ext JS Library 1.1.1
12104  * Copyright(c) 2006-2007, Ext JS, LLC.
12105  *
12106  * Originally Released Under LGPL - original licence link has changed is not relivant.
12107  *
12108  * Fork - LGPL
12109  * <script type="text/javascript">
12110  */
12111 /**
12112  * @class Roo.data.HttpProxy
12113  * @extends Roo.data.DataProxy
12114  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12115  * configured to reference a certain URL.<br><br>
12116  * <p>
12117  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12118  * from which the running page was served.<br><br>
12119  * <p>
12120  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12121  * <p>
12122  * Be aware that to enable the browser to parse an XML document, the server must set
12123  * the Content-Type header in the HTTP response to "text/xml".
12124  * @constructor
12125  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12126  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12127  * will be used to make the request.
12128  */
12129 Roo.data.HttpProxy = function(conn){
12130     Roo.data.HttpProxy.superclass.constructor.call(this);
12131     // is conn a conn config or a real conn?
12132     this.conn = conn;
12133     this.useAjax = !conn || !conn.events;
12134   
12135 };
12136
12137 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12138     // thse are take from connection...
12139     
12140     /**
12141      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12142      */
12143     /**
12144      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12145      * extra parameters to each request made by this object. (defaults to undefined)
12146      */
12147     /**
12148      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12149      *  to each request made by this object. (defaults to undefined)
12150      */
12151     /**
12152      * @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)
12153      */
12154     /**
12155      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12156      */
12157      /**
12158      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12159      * @type Boolean
12160      */
12161   
12162
12163     /**
12164      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12165      * @type Boolean
12166      */
12167     /**
12168      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12169      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12170      * a finer-grained basis than the DataProxy events.
12171      */
12172     getConnection : function(){
12173         return this.useAjax ? Roo.Ajax : this.conn;
12174     },
12175
12176     /**
12177      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12178      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12179      * process that block using the passed callback.
12180      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12181      * for the request to the remote server.
12182      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12183      * object into a block of Roo.data.Records.
12184      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12185      * The function must be passed <ul>
12186      * <li>The Record block object</li>
12187      * <li>The "arg" argument from the load function</li>
12188      * <li>A boolean success indicator</li>
12189      * </ul>
12190      * @param {Object} scope The scope in which to call the callback
12191      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12192      */
12193     load : function(params, reader, callback, scope, arg){
12194         if(this.fireEvent("beforeload", this, params) !== false){
12195             var  o = {
12196                 params : params || {},
12197                 request: {
12198                     callback : callback,
12199                     scope : scope,
12200                     arg : arg
12201                 },
12202                 reader: reader,
12203                 callback : this.loadResponse,
12204                 scope: this
12205             };
12206             if(this.useAjax){
12207                 Roo.applyIf(o, this.conn);
12208                 if(this.activeRequest){
12209                     Roo.Ajax.abort(this.activeRequest);
12210                 }
12211                 this.activeRequest = Roo.Ajax.request(o);
12212             }else{
12213                 this.conn.request(o);
12214             }
12215         }else{
12216             callback.call(scope||this, null, arg, false);
12217         }
12218     },
12219
12220     // private
12221     loadResponse : function(o, success, response){
12222         delete this.activeRequest;
12223         if(!success){
12224             this.fireEvent("loadexception", this, o, response);
12225             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12226             return;
12227         }
12228         var result;
12229         try {
12230             result = o.reader.read(response);
12231         }catch(e){
12232             this.fireEvent("loadexception", this, o, response, e);
12233             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12234             return;
12235         }
12236         
12237         this.fireEvent("load", this, o, o.request.arg);
12238         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12239     },
12240
12241     // private
12242     update : function(dataSet){
12243
12244     },
12245
12246     // private
12247     updateResponse : function(dataSet){
12248
12249     }
12250 });/*
12251  * Based on:
12252  * Ext JS Library 1.1.1
12253  * Copyright(c) 2006-2007, Ext JS, LLC.
12254  *
12255  * Originally Released Under LGPL - original licence link has changed is not relivant.
12256  *
12257  * Fork - LGPL
12258  * <script type="text/javascript">
12259  */
12260
12261 /**
12262  * @class Roo.data.ScriptTagProxy
12263  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12264  * other than the originating domain of the running page.<br><br>
12265  * <p>
12266  * <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
12267  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12268  * <p>
12269  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12270  * source code that is used as the source inside a &lt;script> tag.<br><br>
12271  * <p>
12272  * In order for the browser to process the returned data, the server must wrap the data object
12273  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12274  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12275  * depending on whether the callback name was passed:
12276  * <p>
12277  * <pre><code>
12278 boolean scriptTag = false;
12279 String cb = request.getParameter("callback");
12280 if (cb != null) {
12281     scriptTag = true;
12282     response.setContentType("text/javascript");
12283 } else {
12284     response.setContentType("application/x-json");
12285 }
12286 Writer out = response.getWriter();
12287 if (scriptTag) {
12288     out.write(cb + "(");
12289 }
12290 out.print(dataBlock.toJsonString());
12291 if (scriptTag) {
12292     out.write(");");
12293 }
12294 </pre></code>
12295  *
12296  * @constructor
12297  * @param {Object} config A configuration object.
12298  */
12299 Roo.data.ScriptTagProxy = function(config){
12300     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12301     Roo.apply(this, config);
12302     this.head = document.getElementsByTagName("head")[0];
12303 };
12304
12305 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12306
12307 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12308     /**
12309      * @cfg {String} url The URL from which to request the data object.
12310      */
12311     /**
12312      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12313      */
12314     timeout : 30000,
12315     /**
12316      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12317      * the server the name of the callback function set up by the load call to process the returned data object.
12318      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12319      * javascript output which calls this named function passing the data object as its only parameter.
12320      */
12321     callbackParam : "callback",
12322     /**
12323      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12324      * name to the request.
12325      */
12326     nocache : true,
12327
12328     /**
12329      * Load data from the configured URL, read the data object into
12330      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12331      * process that block using the passed callback.
12332      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12333      * for the request to the remote server.
12334      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12335      * object into a block of Roo.data.Records.
12336      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12337      * The function must be passed <ul>
12338      * <li>The Record block object</li>
12339      * <li>The "arg" argument from the load function</li>
12340      * <li>A boolean success indicator</li>
12341      * </ul>
12342      * @param {Object} scope The scope in which to call the callback
12343      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12344      */
12345     load : function(params, reader, callback, scope, arg){
12346         if(this.fireEvent("beforeload", this, params) !== false){
12347
12348             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12349
12350             var url = this.url;
12351             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12352             if(this.nocache){
12353                 url += "&_dc=" + (new Date().getTime());
12354             }
12355             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12356             var trans = {
12357                 id : transId,
12358                 cb : "stcCallback"+transId,
12359                 scriptId : "stcScript"+transId,
12360                 params : params,
12361                 arg : arg,
12362                 url : url,
12363                 callback : callback,
12364                 scope : scope,
12365                 reader : reader
12366             };
12367             var conn = this;
12368
12369             window[trans.cb] = function(o){
12370                 conn.handleResponse(o, trans);
12371             };
12372
12373             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12374
12375             if(this.autoAbort !== false){
12376                 this.abort();
12377             }
12378
12379             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12380
12381             var script = document.createElement("script");
12382             script.setAttribute("src", url);
12383             script.setAttribute("type", "text/javascript");
12384             script.setAttribute("id", trans.scriptId);
12385             this.head.appendChild(script);
12386
12387             this.trans = trans;
12388         }else{
12389             callback.call(scope||this, null, arg, false);
12390         }
12391     },
12392
12393     // private
12394     isLoading : function(){
12395         return this.trans ? true : false;
12396     },
12397
12398     /**
12399      * Abort the current server request.
12400      */
12401     abort : function(){
12402         if(this.isLoading()){
12403             this.destroyTrans(this.trans);
12404         }
12405     },
12406
12407     // private
12408     destroyTrans : function(trans, isLoaded){
12409         this.head.removeChild(document.getElementById(trans.scriptId));
12410         clearTimeout(trans.timeoutId);
12411         if(isLoaded){
12412             window[trans.cb] = undefined;
12413             try{
12414                 delete window[trans.cb];
12415             }catch(e){}
12416         }else{
12417             // if hasn't been loaded, wait for load to remove it to prevent script error
12418             window[trans.cb] = function(){
12419                 window[trans.cb] = undefined;
12420                 try{
12421                     delete window[trans.cb];
12422                 }catch(e){}
12423             };
12424         }
12425     },
12426
12427     // private
12428     handleResponse : function(o, trans){
12429         this.trans = false;
12430         this.destroyTrans(trans, true);
12431         var result;
12432         try {
12433             result = trans.reader.readRecords(o);
12434         }catch(e){
12435             this.fireEvent("loadexception", this, o, trans.arg, e);
12436             trans.callback.call(trans.scope||window, null, trans.arg, false);
12437             return;
12438         }
12439         this.fireEvent("load", this, o, trans.arg);
12440         trans.callback.call(trans.scope||window, result, trans.arg, true);
12441     },
12442
12443     // private
12444     handleFailure : function(trans){
12445         this.trans = false;
12446         this.destroyTrans(trans, false);
12447         this.fireEvent("loadexception", this, null, trans.arg);
12448         trans.callback.call(trans.scope||window, null, trans.arg, false);
12449     }
12450 });/*
12451  * Based on:
12452  * Ext JS Library 1.1.1
12453  * Copyright(c) 2006-2007, Ext JS, LLC.
12454  *
12455  * Originally Released Under LGPL - original licence link has changed is not relivant.
12456  *
12457  * Fork - LGPL
12458  * <script type="text/javascript">
12459  */
12460
12461 /**
12462  * @class Roo.data.JsonReader
12463  * @extends Roo.data.DataReader
12464  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12465  * based on mappings in a provided Roo.data.Record constructor.
12466  * 
12467  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12468  * in the reply previously. 
12469  * 
12470  * <p>
12471  * Example code:
12472  * <pre><code>
12473 var RecordDef = Roo.data.Record.create([
12474     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12475     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12476 ]);
12477 var myReader = new Roo.data.JsonReader({
12478     totalProperty: "results",    // The property which contains the total dataset size (optional)
12479     root: "rows",                // The property which contains an Array of row objects
12480     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12481 }, RecordDef);
12482 </code></pre>
12483  * <p>
12484  * This would consume a JSON file like this:
12485  * <pre><code>
12486 { 'results': 2, 'rows': [
12487     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12488     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12489 }
12490 </code></pre>
12491  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12492  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12493  * paged from the remote server.
12494  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12495  * @cfg {String} root name of the property which contains the Array of row objects.
12496  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12497  * @cfg {Array} fields Array of field definition objects
12498  * @constructor
12499  * Create a new JsonReader
12500  * @param {Object} meta Metadata configuration options
12501  * @param {Object} recordType Either an Array of field definition objects,
12502  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12503  */
12504 Roo.data.JsonReader = function(meta, recordType){
12505     
12506     meta = meta || {};
12507     // set some defaults:
12508     Roo.applyIf(meta, {
12509         totalProperty: 'total',
12510         successProperty : 'success',
12511         root : 'data',
12512         id : 'id'
12513     });
12514     
12515     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12516 };
12517 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12518     
12519     /**
12520      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12521      * Used by Store query builder to append _requestMeta to params.
12522      * 
12523      */
12524     metaFromRemote : false,
12525     /**
12526      * This method is only used by a DataProxy which has retrieved data from a remote server.
12527      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12528      * @return {Object} data A data block which is used by an Roo.data.Store object as
12529      * a cache of Roo.data.Records.
12530      */
12531     read : function(response){
12532         var json = response.responseText;
12533        
12534         var o = /* eval:var:o */ eval("("+json+")");
12535         if(!o) {
12536             throw {message: "JsonReader.read: Json object not found"};
12537         }
12538         
12539         if(o.metaData){
12540             
12541             delete this.ef;
12542             this.metaFromRemote = true;
12543             this.meta = o.metaData;
12544             this.recordType = Roo.data.Record.create(o.metaData.fields);
12545             this.onMetaChange(this.meta, this.recordType, o);
12546         }
12547         return this.readRecords(o);
12548     },
12549
12550     // private function a store will implement
12551     onMetaChange : function(meta, recordType, o){
12552
12553     },
12554
12555     /**
12556          * @ignore
12557          */
12558     simpleAccess: function(obj, subsc) {
12559         return obj[subsc];
12560     },
12561
12562         /**
12563          * @ignore
12564          */
12565     getJsonAccessor: function(){
12566         var re = /[\[\.]/;
12567         return function(expr) {
12568             try {
12569                 return(re.test(expr))
12570                     ? new Function("obj", "return obj." + expr)
12571                     : function(obj){
12572                         return obj[expr];
12573                     };
12574             } catch(e){}
12575             return Roo.emptyFn;
12576         };
12577     }(),
12578
12579     /**
12580      * Create a data block containing Roo.data.Records from an XML document.
12581      * @param {Object} o An object which contains an Array of row objects in the property specified
12582      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12583      * which contains the total size of the dataset.
12584      * @return {Object} data A data block which is used by an Roo.data.Store object as
12585      * a cache of Roo.data.Records.
12586      */
12587     readRecords : function(o){
12588         /**
12589          * After any data loads, the raw JSON data is available for further custom processing.
12590          * @type Object
12591          */
12592         this.o = o;
12593         var s = this.meta, Record = this.recordType,
12594             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12595
12596 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12597         if (!this.ef) {
12598             if(s.totalProperty) {
12599                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12600                 }
12601                 if(s.successProperty) {
12602                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12603                 }
12604                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12605                 if (s.id) {
12606                         var g = this.getJsonAccessor(s.id);
12607                         this.getId = function(rec) {
12608                                 var r = g(rec);  
12609                                 return (r === undefined || r === "") ? null : r;
12610                         };
12611                 } else {
12612                         this.getId = function(){return null;};
12613                 }
12614             this.ef = [];
12615             for(var jj = 0; jj < fl; jj++){
12616                 f = fi[jj];
12617                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12618                 this.ef[jj] = this.getJsonAccessor(map);
12619             }
12620         }
12621
12622         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12623         if(s.totalProperty){
12624             var vt = parseInt(this.getTotal(o), 10);
12625             if(!isNaN(vt)){
12626                 totalRecords = vt;
12627             }
12628         }
12629         if(s.successProperty){
12630             var vs = this.getSuccess(o);
12631             if(vs === false || vs === 'false'){
12632                 success = false;
12633             }
12634         }
12635         var records = [];
12636         for(var i = 0; i < c; i++){
12637                 var n = root[i];
12638             var values = {};
12639             var id = this.getId(n);
12640             for(var j = 0; j < fl; j++){
12641                 f = fi[j];
12642             var v = this.ef[j](n);
12643             if (!f.convert) {
12644                 Roo.log('missing convert for ' + f.name);
12645                 Roo.log(f);
12646                 continue;
12647             }
12648             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12649             }
12650             var record = new Record(values, id);
12651             record.json = n;
12652             records[i] = record;
12653         }
12654         return {
12655             raw : o,
12656             success : success,
12657             records : records,
12658             totalRecords : totalRecords
12659         };
12660     }
12661 });/*
12662  * Based on:
12663  * Ext JS Library 1.1.1
12664  * Copyright(c) 2006-2007, Ext JS, LLC.
12665  *
12666  * Originally Released Under LGPL - original licence link has changed is not relivant.
12667  *
12668  * Fork - LGPL
12669  * <script type="text/javascript">
12670  */
12671
12672 /**
12673  * @class Roo.data.ArrayReader
12674  * @extends Roo.data.DataReader
12675  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12676  * Each element of that Array represents a row of data fields. The
12677  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12678  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12679  * <p>
12680  * Example code:.
12681  * <pre><code>
12682 var RecordDef = Roo.data.Record.create([
12683     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12684     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12685 ]);
12686 var myReader = new Roo.data.ArrayReader({
12687     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12688 }, RecordDef);
12689 </code></pre>
12690  * <p>
12691  * This would consume an Array like this:
12692  * <pre><code>
12693 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12694   </code></pre>
12695  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12696  * @constructor
12697  * Create a new JsonReader
12698  * @param {Object} meta Metadata configuration options.
12699  * @param {Object} recordType Either an Array of field definition objects
12700  * as specified to {@link Roo.data.Record#create},
12701  * or an {@link Roo.data.Record} object
12702  * created using {@link Roo.data.Record#create}.
12703  */
12704 Roo.data.ArrayReader = function(meta, recordType){
12705     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12706 };
12707
12708 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12709     /**
12710      * Create a data block containing Roo.data.Records from an XML document.
12711      * @param {Object} o An Array of row objects which represents the dataset.
12712      * @return {Object} data A data block which is used by an Roo.data.Store object as
12713      * a cache of Roo.data.Records.
12714      */
12715     readRecords : function(o){
12716         var sid = this.meta ? this.meta.id : null;
12717         var recordType = this.recordType, fields = recordType.prototype.fields;
12718         var records = [];
12719         var root = o;
12720             for(var i = 0; i < root.length; i++){
12721                     var n = root[i];
12722                 var values = {};
12723                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12724                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12725                 var f = fields.items[j];
12726                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12727                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12728                 v = f.convert(v);
12729                 values[f.name] = v;
12730             }
12731                 var record = new recordType(values, id);
12732                 record.json = n;
12733                 records[records.length] = record;
12734             }
12735             return {
12736                 records : records,
12737                 totalRecords : records.length
12738             };
12739     }
12740 });/*
12741  * - LGPL
12742  * * 
12743  */
12744
12745 /**
12746  * @class Roo.bootstrap.ComboBox
12747  * @extends Roo.bootstrap.TriggerField
12748  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12749  * @cfg {Boolean} append (true|false) default false
12750  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12751  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12752  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12753  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12754  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12755  * @cfg {Boolean} animate default true
12756  * @cfg {Boolean} emptyResultText only for touch device
12757  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12758  * @cfg {String} emptyTitle default ''
12759  * @constructor
12760  * Create a new ComboBox.
12761  * @param {Object} config Configuration options
12762  */
12763 Roo.bootstrap.ComboBox = function(config){
12764     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12765     this.addEvents({
12766         /**
12767          * @event expand
12768          * Fires when the dropdown list is expanded
12769         * @param {Roo.bootstrap.ComboBox} combo This combo box
12770         */
12771         'expand' : true,
12772         /**
12773          * @event collapse
12774          * Fires when the dropdown list is collapsed
12775         * @param {Roo.bootstrap.ComboBox} combo This combo box
12776         */
12777         'collapse' : true,
12778         /**
12779          * @event beforeselect
12780          * Fires before a list item is selected. Return false to cancel the selection.
12781         * @param {Roo.bootstrap.ComboBox} combo This combo box
12782         * @param {Roo.data.Record} record The data record returned from the underlying store
12783         * @param {Number} index The index of the selected item in the dropdown list
12784         */
12785         'beforeselect' : true,
12786         /**
12787          * @event select
12788          * Fires when a list item is selected
12789         * @param {Roo.bootstrap.ComboBox} combo This combo box
12790         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12791         * @param {Number} index The index of the selected item in the dropdown list
12792         */
12793         'select' : true,
12794         /**
12795          * @event beforequery
12796          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12797          * The event object passed has these properties:
12798         * @param {Roo.bootstrap.ComboBox} combo This combo box
12799         * @param {String} query The query
12800         * @param {Boolean} forceAll true to force "all" query
12801         * @param {Boolean} cancel true to cancel the query
12802         * @param {Object} e The query event object
12803         */
12804         'beforequery': true,
12805          /**
12806          * @event add
12807          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12808         * @param {Roo.bootstrap.ComboBox} combo This combo box
12809         */
12810         'add' : true,
12811         /**
12812          * @event edit
12813          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12814         * @param {Roo.bootstrap.ComboBox} combo This combo box
12815         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12816         */
12817         'edit' : true,
12818         /**
12819          * @event remove
12820          * Fires when the remove value from the combobox array
12821         * @param {Roo.bootstrap.ComboBox} combo This combo box
12822         */
12823         'remove' : true,
12824         /**
12825          * @event afterremove
12826          * Fires when the remove value from the combobox array
12827         * @param {Roo.bootstrap.ComboBox} combo This combo box
12828         */
12829         'afterremove' : true,
12830         /**
12831          * @event specialfilter
12832          * Fires when specialfilter
12833             * @param {Roo.bootstrap.ComboBox} combo This combo box
12834             */
12835         'specialfilter' : true,
12836         /**
12837          * @event tick
12838          * Fires when tick the element
12839             * @param {Roo.bootstrap.ComboBox} combo This combo box
12840             */
12841         'tick' : true,
12842         /**
12843          * @event touchviewdisplay
12844          * Fires when touch view require special display (default is using displayField)
12845             * @param {Roo.bootstrap.ComboBox} combo This combo box
12846             * @param {Object} cfg set html .
12847             */
12848         'touchviewdisplay' : true
12849         
12850     });
12851     
12852     this.item = [];
12853     this.tickItems = [];
12854     
12855     this.selectedIndex = -1;
12856     if(this.mode == 'local'){
12857         if(config.queryDelay === undefined){
12858             this.queryDelay = 10;
12859         }
12860         if(config.minChars === undefined){
12861             this.minChars = 0;
12862         }
12863     }
12864 };
12865
12866 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12867      
12868     /**
12869      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12870      * rendering into an Roo.Editor, defaults to false)
12871      */
12872     /**
12873      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12874      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12875      */
12876     /**
12877      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12878      */
12879     /**
12880      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12881      * the dropdown list (defaults to undefined, with no header element)
12882      */
12883
12884      /**
12885      * @cfg {String/Roo.Template} tpl The template to use to render the output
12886      */
12887      
12888      /**
12889      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12890      */
12891     listWidth: undefined,
12892     /**
12893      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12894      * mode = 'remote' or 'text' if mode = 'local')
12895      */
12896     displayField: undefined,
12897     
12898     /**
12899      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12900      * mode = 'remote' or 'value' if mode = 'local'). 
12901      * Note: use of a valueField requires the user make a selection
12902      * in order for a value to be mapped.
12903      */
12904     valueField: undefined,
12905     /**
12906      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12907      */
12908     modalTitle : '',
12909     
12910     /**
12911      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12912      * field's data value (defaults to the underlying DOM element's name)
12913      */
12914     hiddenName: undefined,
12915     /**
12916      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12917      */
12918     listClass: '',
12919     /**
12920      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12921      */
12922     selectedClass: 'active',
12923     
12924     /**
12925      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12926      */
12927     shadow:'sides',
12928     /**
12929      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12930      * anchor positions (defaults to 'tl-bl')
12931      */
12932     listAlign: 'tl-bl?',
12933     /**
12934      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12935      */
12936     maxHeight: 300,
12937     /**
12938      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12939      * query specified by the allQuery config option (defaults to 'query')
12940      */
12941     triggerAction: 'query',
12942     /**
12943      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12944      * (defaults to 4, does not apply if editable = false)
12945      */
12946     minChars : 4,
12947     /**
12948      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12949      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12950      */
12951     typeAhead: false,
12952     /**
12953      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12954      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12955      */
12956     queryDelay: 500,
12957     /**
12958      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12959      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12960      */
12961     pageSize: 0,
12962     /**
12963      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12964      * when editable = true (defaults to false)
12965      */
12966     selectOnFocus:false,
12967     /**
12968      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12969      */
12970     queryParam: 'query',
12971     /**
12972      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12973      * when mode = 'remote' (defaults to 'Loading...')
12974      */
12975     loadingText: 'Loading...',
12976     /**
12977      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12978      */
12979     resizable: false,
12980     /**
12981      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12982      */
12983     handleHeight : 8,
12984     /**
12985      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12986      * traditional select (defaults to true)
12987      */
12988     editable: true,
12989     /**
12990      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12991      */
12992     allQuery: '',
12993     /**
12994      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12995      */
12996     mode: 'remote',
12997     /**
12998      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12999      * listWidth has a higher value)
13000      */
13001     minListWidth : 70,
13002     /**
13003      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13004      * allow the user to set arbitrary text into the field (defaults to false)
13005      */
13006     forceSelection:false,
13007     /**
13008      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13009      * if typeAhead = true (defaults to 250)
13010      */
13011     typeAheadDelay : 250,
13012     /**
13013      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13014      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13015      */
13016     valueNotFoundText : undefined,
13017     /**
13018      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13019      */
13020     blockFocus : false,
13021     
13022     /**
13023      * @cfg {Boolean} disableClear Disable showing of clear button.
13024      */
13025     disableClear : false,
13026     /**
13027      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13028      */
13029     alwaysQuery : false,
13030     
13031     /**
13032      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13033      */
13034     multiple : false,
13035     
13036     /**
13037      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13038      */
13039     invalidClass : "has-warning",
13040     
13041     /**
13042      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13043      */
13044     validClass : "has-success",
13045     
13046     /**
13047      * @cfg {Boolean} specialFilter (true|false) special filter default false
13048      */
13049     specialFilter : false,
13050     
13051     /**
13052      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13053      */
13054     mobileTouchView : true,
13055     
13056     /**
13057      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13058      */
13059     useNativeIOS : false,
13060     
13061     ios_options : false,
13062     
13063     //private
13064     addicon : false,
13065     editicon: false,
13066     
13067     page: 0,
13068     hasQuery: false,
13069     append: false,
13070     loadNext: false,
13071     autoFocus : true,
13072     tickable : false,
13073     btnPosition : 'right',
13074     triggerList : true,
13075     showToggleBtn : true,
13076     animate : true,
13077     emptyResultText: 'Empty',
13078     triggerText : 'Select',
13079     emptyTitle : '',
13080     
13081     // element that contains real text value.. (when hidden is used..)
13082     
13083     getAutoCreate : function()
13084     {   
13085         var cfg = false;
13086         //render
13087         /*
13088          * Render classic select for iso
13089          */
13090         
13091         if(Roo.isIOS && this.useNativeIOS){
13092             cfg = this.getAutoCreateNativeIOS();
13093             return cfg;
13094         }
13095         
13096         /*
13097          * Touch Devices
13098          */
13099         
13100         if(Roo.isTouch && this.mobileTouchView){
13101             cfg = this.getAutoCreateTouchView();
13102             return cfg;;
13103         }
13104         
13105         /*
13106          *  Normal ComboBox
13107          */
13108         if(!this.tickable){
13109             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13110             return cfg;
13111         }
13112         
13113         /*
13114          *  ComboBox with tickable selections
13115          */
13116              
13117         var align = this.labelAlign || this.parentLabelAlign();
13118         
13119         cfg = {
13120             cls : 'form-group roo-combobox-tickable' //input-group
13121         };
13122         
13123         var btn_text_select = '';
13124         var btn_text_done = '';
13125         var btn_text_cancel = '';
13126         
13127         if (this.btn_text_show) {
13128             btn_text_select = 'Select';
13129             btn_text_done = 'Done';
13130             btn_text_cancel = 'Cancel'; 
13131         }
13132         
13133         var buttons = {
13134             tag : 'div',
13135             cls : 'tickable-buttons',
13136             cn : [
13137                 {
13138                     tag : 'button',
13139                     type : 'button',
13140                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13141                     //html : this.triggerText
13142                     html: btn_text_select
13143                 },
13144                 {
13145                     tag : 'button',
13146                     type : 'button',
13147                     name : 'ok',
13148                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13149                     //html : 'Done'
13150                     html: btn_text_done
13151                 },
13152                 {
13153                     tag : 'button',
13154                     type : 'button',
13155                     name : 'cancel',
13156                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13157                     //html : 'Cancel'
13158                     html: btn_text_cancel
13159                 }
13160             ]
13161         };
13162         
13163         if(this.editable){
13164             buttons.cn.unshift({
13165                 tag: 'input',
13166                 cls: 'roo-select2-search-field-input'
13167             });
13168         }
13169         
13170         var _this = this;
13171         
13172         Roo.each(buttons.cn, function(c){
13173             if (_this.size) {
13174                 c.cls += ' btn-' + _this.size;
13175             }
13176
13177             if (_this.disabled) {
13178                 c.disabled = true;
13179             }
13180         });
13181         
13182         var box = {
13183             tag: 'div',
13184             cn: [
13185                 {
13186                     tag: 'input',
13187                     type : 'hidden',
13188                     cls: 'form-hidden-field'
13189                 },
13190                 {
13191                     tag: 'ul',
13192                     cls: 'roo-select2-choices',
13193                     cn:[
13194                         {
13195                             tag: 'li',
13196                             cls: 'roo-select2-search-field',
13197                             cn: [
13198                                 buttons
13199                             ]
13200                         }
13201                     ]
13202                 }
13203             ]
13204         };
13205         
13206         var combobox = {
13207             cls: 'roo-select2-container input-group roo-select2-container-multi',
13208             cn: [
13209                 box
13210 //                {
13211 //                    tag: 'ul',
13212 //                    cls: 'typeahead typeahead-long dropdown-menu',
13213 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13214 //                }
13215             ]
13216         };
13217         
13218         if(this.hasFeedback && !this.allowBlank){
13219             
13220             var feedback = {
13221                 tag: 'span',
13222                 cls: 'glyphicon form-control-feedback'
13223             };
13224
13225             combobox.cn.push(feedback);
13226         }
13227         
13228         
13229         if (align ==='left' && this.fieldLabel.length) {
13230             
13231             cfg.cls += ' roo-form-group-label-left';
13232             
13233             cfg.cn = [
13234                 {
13235                     tag : 'i',
13236                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13237                     tooltip : 'This field is required'
13238                 },
13239                 {
13240                     tag: 'label',
13241                     'for' :  id,
13242                     cls : 'control-label',
13243                     html : this.fieldLabel
13244
13245                 },
13246                 {
13247                     cls : "", 
13248                     cn: [
13249                         combobox
13250                     ]
13251                 }
13252
13253             ];
13254             
13255             var labelCfg = cfg.cn[1];
13256             var contentCfg = cfg.cn[2];
13257             
13258
13259             if(this.indicatorpos == 'right'){
13260                 
13261                 cfg.cn = [
13262                     {
13263                         tag: 'label',
13264                         'for' :  id,
13265                         cls : 'control-label',
13266                         cn : [
13267                             {
13268                                 tag : 'span',
13269                                 html : this.fieldLabel
13270                             },
13271                             {
13272                                 tag : 'i',
13273                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13274                                 tooltip : 'This field is required'
13275                             }
13276                         ]
13277                     },
13278                     {
13279                         cls : "",
13280                         cn: [
13281                             combobox
13282                         ]
13283                     }
13284
13285                 ];
13286                 
13287                 
13288                 
13289                 labelCfg = cfg.cn[0];
13290                 contentCfg = cfg.cn[1];
13291             
13292             }
13293             
13294             if(this.labelWidth > 12){
13295                 labelCfg.style = "width: " + this.labelWidth + 'px';
13296             }
13297             
13298             if(this.labelWidth < 13 && this.labelmd == 0){
13299                 this.labelmd = this.labelWidth;
13300             }
13301             
13302             if(this.labellg > 0){
13303                 labelCfg.cls += ' col-lg-' + this.labellg;
13304                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13305             }
13306             
13307             if(this.labelmd > 0){
13308                 labelCfg.cls += ' col-md-' + this.labelmd;
13309                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13310             }
13311             
13312             if(this.labelsm > 0){
13313                 labelCfg.cls += ' col-sm-' + this.labelsm;
13314                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13315             }
13316             
13317             if(this.labelxs > 0){
13318                 labelCfg.cls += ' col-xs-' + this.labelxs;
13319                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13320             }
13321                 
13322                 
13323         } else if ( this.fieldLabel.length) {
13324 //                Roo.log(" label");
13325                  cfg.cn = [
13326                     {
13327                         tag : 'i',
13328                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13329                         tooltip : 'This field is required'
13330                     },
13331                     {
13332                         tag: 'label',
13333                         //cls : 'input-group-addon',
13334                         html : this.fieldLabel
13335                     },
13336                     combobox
13337                 ];
13338                 
13339                 if(this.indicatorpos == 'right'){
13340                     cfg.cn = [
13341                         {
13342                             tag: 'label',
13343                             //cls : 'input-group-addon',
13344                             html : this.fieldLabel
13345                         },
13346                         {
13347                             tag : 'i',
13348                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13349                             tooltip : 'This field is required'
13350                         },
13351                         combobox
13352                     ];
13353                     
13354                 }
13355
13356         } else {
13357             
13358 //                Roo.log(" no label && no align");
13359                 cfg = combobox
13360                      
13361                 
13362         }
13363          
13364         var settings=this;
13365         ['xs','sm','md','lg'].map(function(size){
13366             if (settings[size]) {
13367                 cfg.cls += ' col-' + size + '-' + settings[size];
13368             }
13369         });
13370         
13371         return cfg;
13372         
13373     },
13374     
13375     _initEventsCalled : false,
13376     
13377     // private
13378     initEvents: function()
13379     {   
13380         if (this._initEventsCalled) { // as we call render... prevent looping...
13381             return;
13382         }
13383         this._initEventsCalled = true;
13384         
13385         if (!this.store) {
13386             throw "can not find store for combo";
13387         }
13388         
13389         this.indicator = this.indicatorEl();
13390         
13391         this.store = Roo.factory(this.store, Roo.data);
13392         this.store.parent = this;
13393         
13394         // if we are building from html. then this element is so complex, that we can not really
13395         // use the rendered HTML.
13396         // so we have to trash and replace the previous code.
13397         if (Roo.XComponent.build_from_html) {
13398             // remove this element....
13399             var e = this.el.dom, k=0;
13400             while (e ) { e = e.previousSibling;  ++k;}
13401
13402             this.el.remove();
13403             
13404             this.el=false;
13405             this.rendered = false;
13406             
13407             this.render(this.parent().getChildContainer(true), k);
13408         }
13409         
13410         if(Roo.isIOS && this.useNativeIOS){
13411             this.initIOSView();
13412             return;
13413         }
13414         
13415         /*
13416          * Touch Devices
13417          */
13418         
13419         if(Roo.isTouch && this.mobileTouchView){
13420             this.initTouchView();
13421             return;
13422         }
13423         
13424         if(this.tickable){
13425             this.initTickableEvents();
13426             return;
13427         }
13428         
13429         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13430         
13431         if(this.hiddenName){
13432             
13433             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13434             
13435             this.hiddenField.dom.value =
13436                 this.hiddenValue !== undefined ? this.hiddenValue :
13437                 this.value !== undefined ? this.value : '';
13438
13439             // prevent input submission
13440             this.el.dom.removeAttribute('name');
13441             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13442              
13443              
13444         }
13445         //if(Roo.isGecko){
13446         //    this.el.dom.setAttribute('autocomplete', 'off');
13447         //}
13448         
13449         var cls = 'x-combo-list';
13450         
13451         //this.list = new Roo.Layer({
13452         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13453         //});
13454         
13455         var _this = this;
13456         
13457         (function(){
13458             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13459             _this.list.setWidth(lw);
13460         }).defer(100);
13461         
13462         this.list.on('mouseover', this.onViewOver, this);
13463         this.list.on('mousemove', this.onViewMove, this);
13464         this.list.on('scroll', this.onViewScroll, this);
13465         
13466         /*
13467         this.list.swallowEvent('mousewheel');
13468         this.assetHeight = 0;
13469
13470         if(this.title){
13471             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13472             this.assetHeight += this.header.getHeight();
13473         }
13474
13475         this.innerList = this.list.createChild({cls:cls+'-inner'});
13476         this.innerList.on('mouseover', this.onViewOver, this);
13477         this.innerList.on('mousemove', this.onViewMove, this);
13478         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13479         
13480         if(this.allowBlank && !this.pageSize && !this.disableClear){
13481             this.footer = this.list.createChild({cls:cls+'-ft'});
13482             this.pageTb = new Roo.Toolbar(this.footer);
13483            
13484         }
13485         if(this.pageSize){
13486             this.footer = this.list.createChild({cls:cls+'-ft'});
13487             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13488                     {pageSize: this.pageSize});
13489             
13490         }
13491         
13492         if (this.pageTb && this.allowBlank && !this.disableClear) {
13493             var _this = this;
13494             this.pageTb.add(new Roo.Toolbar.Fill(), {
13495                 cls: 'x-btn-icon x-btn-clear',
13496                 text: '&#160;',
13497                 handler: function()
13498                 {
13499                     _this.collapse();
13500                     _this.clearValue();
13501                     _this.onSelect(false, -1);
13502                 }
13503             });
13504         }
13505         if (this.footer) {
13506             this.assetHeight += this.footer.getHeight();
13507         }
13508         */
13509             
13510         if(!this.tpl){
13511             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13512         }
13513
13514         this.view = new Roo.View(this.list, this.tpl, {
13515             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13516         });
13517         //this.view.wrapEl.setDisplayed(false);
13518         this.view.on('click', this.onViewClick, this);
13519         
13520         
13521         this.store.on('beforeload', this.onBeforeLoad, this);
13522         this.store.on('load', this.onLoad, this);
13523         this.store.on('loadexception', this.onLoadException, this);
13524         /*
13525         if(this.resizable){
13526             this.resizer = new Roo.Resizable(this.list,  {
13527                pinned:true, handles:'se'
13528             });
13529             this.resizer.on('resize', function(r, w, h){
13530                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13531                 this.listWidth = w;
13532                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13533                 this.restrictHeight();
13534             }, this);
13535             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13536         }
13537         */
13538         if(!this.editable){
13539             this.editable = true;
13540             this.setEditable(false);
13541         }
13542         
13543         /*
13544         
13545         if (typeof(this.events.add.listeners) != 'undefined') {
13546             
13547             this.addicon = this.wrap.createChild(
13548                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13549        
13550             this.addicon.on('click', function(e) {
13551                 this.fireEvent('add', this);
13552             }, this);
13553         }
13554         if (typeof(this.events.edit.listeners) != 'undefined') {
13555             
13556             this.editicon = this.wrap.createChild(
13557                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13558             if (this.addicon) {
13559                 this.editicon.setStyle('margin-left', '40px');
13560             }
13561             this.editicon.on('click', function(e) {
13562                 
13563                 // we fire even  if inothing is selected..
13564                 this.fireEvent('edit', this, this.lastData );
13565                 
13566             }, this);
13567         }
13568         */
13569         
13570         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13571             "up" : function(e){
13572                 this.inKeyMode = true;
13573                 this.selectPrev();
13574             },
13575
13576             "down" : function(e){
13577                 if(!this.isExpanded()){
13578                     this.onTriggerClick();
13579                 }else{
13580                     this.inKeyMode = true;
13581                     this.selectNext();
13582                 }
13583             },
13584
13585             "enter" : function(e){
13586 //                this.onViewClick();
13587                 //return true;
13588                 this.collapse();
13589                 
13590                 if(this.fireEvent("specialkey", this, e)){
13591                     this.onViewClick(false);
13592                 }
13593                 
13594                 return true;
13595             },
13596
13597             "esc" : function(e){
13598                 this.collapse();
13599             },
13600
13601             "tab" : function(e){
13602                 this.collapse();
13603                 
13604                 if(this.fireEvent("specialkey", this, e)){
13605                     this.onViewClick(false);
13606                 }
13607                 
13608                 return true;
13609             },
13610
13611             scope : this,
13612
13613             doRelay : function(foo, bar, hname){
13614                 if(hname == 'down' || this.scope.isExpanded()){
13615                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13616                 }
13617                 return true;
13618             },
13619
13620             forceKeyDown: true
13621         });
13622         
13623         
13624         this.queryDelay = Math.max(this.queryDelay || 10,
13625                 this.mode == 'local' ? 10 : 250);
13626         
13627         
13628         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13629         
13630         if(this.typeAhead){
13631             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13632         }
13633         if(this.editable !== false){
13634             this.inputEl().on("keyup", this.onKeyUp, this);
13635         }
13636         if(this.forceSelection){
13637             this.inputEl().on('blur', this.doForce, this);
13638         }
13639         
13640         if(this.multiple){
13641             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13642             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13643         }
13644     },
13645     
13646     initTickableEvents: function()
13647     {   
13648         this.createList();
13649         
13650         if(this.hiddenName){
13651             
13652             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13653             
13654             this.hiddenField.dom.value =
13655                 this.hiddenValue !== undefined ? this.hiddenValue :
13656                 this.value !== undefined ? this.value : '';
13657
13658             // prevent input submission
13659             this.el.dom.removeAttribute('name');
13660             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13661              
13662              
13663         }
13664         
13665 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13666         
13667         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13668         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13669         if(this.triggerList){
13670             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13671         }
13672          
13673         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13674         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13675         
13676         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13677         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13678         
13679         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13680         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13681         
13682         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13683         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13684         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13685         
13686         this.okBtn.hide();
13687         this.cancelBtn.hide();
13688         
13689         var _this = this;
13690         
13691         (function(){
13692             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13693             _this.list.setWidth(lw);
13694         }).defer(100);
13695         
13696         this.list.on('mouseover', this.onViewOver, this);
13697         this.list.on('mousemove', this.onViewMove, this);
13698         
13699         this.list.on('scroll', this.onViewScroll, this);
13700         
13701         if(!this.tpl){
13702             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13703                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13704         }
13705
13706         this.view = new Roo.View(this.list, this.tpl, {
13707             singleSelect:true,
13708             tickable:true,
13709             parent:this,
13710             store: this.store,
13711             selectedClass: this.selectedClass
13712         });
13713         
13714         //this.view.wrapEl.setDisplayed(false);
13715         this.view.on('click', this.onViewClick, this);
13716         
13717         
13718         
13719         this.store.on('beforeload', this.onBeforeLoad, this);
13720         this.store.on('load', this.onLoad, this);
13721         this.store.on('loadexception', this.onLoadException, this);
13722         
13723         if(this.editable){
13724             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13725                 "up" : function(e){
13726                     this.inKeyMode = true;
13727                     this.selectPrev();
13728                 },
13729
13730                 "down" : function(e){
13731                     this.inKeyMode = true;
13732                     this.selectNext();
13733                 },
13734
13735                 "enter" : function(e){
13736                     if(this.fireEvent("specialkey", this, e)){
13737                         this.onViewClick(false);
13738                     }
13739                     
13740                     return true;
13741                 },
13742
13743                 "esc" : function(e){
13744                     this.onTickableFooterButtonClick(e, false, false);
13745                 },
13746
13747                 "tab" : function(e){
13748                     this.fireEvent("specialkey", this, e);
13749                     
13750                     this.onTickableFooterButtonClick(e, false, false);
13751                     
13752                     return true;
13753                 },
13754
13755                 scope : this,
13756
13757                 doRelay : function(e, fn, key){
13758                     if(this.scope.isExpanded()){
13759                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13760                     }
13761                     return true;
13762                 },
13763
13764                 forceKeyDown: true
13765             });
13766         }
13767         
13768         this.queryDelay = Math.max(this.queryDelay || 10,
13769                 this.mode == 'local' ? 10 : 250);
13770         
13771         
13772         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13773         
13774         if(this.typeAhead){
13775             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13776         }
13777         
13778         if(this.editable !== false){
13779             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13780         }
13781         
13782         this.indicator = this.indicatorEl();
13783         
13784         if(this.indicator){
13785             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13786             this.indicator.hide();
13787         }
13788         
13789     },
13790
13791     onDestroy : function(){
13792         if(this.view){
13793             this.view.setStore(null);
13794             this.view.el.removeAllListeners();
13795             this.view.el.remove();
13796             this.view.purgeListeners();
13797         }
13798         if(this.list){
13799             this.list.dom.innerHTML  = '';
13800         }
13801         
13802         if(this.store){
13803             this.store.un('beforeload', this.onBeforeLoad, this);
13804             this.store.un('load', this.onLoad, this);
13805             this.store.un('loadexception', this.onLoadException, this);
13806         }
13807         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13808     },
13809
13810     // private
13811     fireKey : function(e){
13812         if(e.isNavKeyPress() && !this.list.isVisible()){
13813             this.fireEvent("specialkey", this, e);
13814         }
13815     },
13816
13817     // private
13818     onResize: function(w, h){
13819 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13820 //        
13821 //        if(typeof w != 'number'){
13822 //            // we do not handle it!?!?
13823 //            return;
13824 //        }
13825 //        var tw = this.trigger.getWidth();
13826 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13827 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13828 //        var x = w - tw;
13829 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13830 //            
13831 //        //this.trigger.setStyle('left', x+'px');
13832 //        
13833 //        if(this.list && this.listWidth === undefined){
13834 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13835 //            this.list.setWidth(lw);
13836 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13837 //        }
13838         
13839     
13840         
13841     },
13842
13843     /**
13844      * Allow or prevent the user from directly editing the field text.  If false is passed,
13845      * the user will only be able to select from the items defined in the dropdown list.  This method
13846      * is the runtime equivalent of setting the 'editable' config option at config time.
13847      * @param {Boolean} value True to allow the user to directly edit the field text
13848      */
13849     setEditable : function(value){
13850         if(value == this.editable){
13851             return;
13852         }
13853         this.editable = value;
13854         if(!value){
13855             this.inputEl().dom.setAttribute('readOnly', true);
13856             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13857             this.inputEl().addClass('x-combo-noedit');
13858         }else{
13859             this.inputEl().dom.setAttribute('readOnly', false);
13860             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13861             this.inputEl().removeClass('x-combo-noedit');
13862         }
13863     },
13864
13865     // private
13866     
13867     onBeforeLoad : function(combo,opts){
13868         if(!this.hasFocus){
13869             return;
13870         }
13871          if (!opts.add) {
13872             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13873          }
13874         this.restrictHeight();
13875         this.selectedIndex = -1;
13876     },
13877
13878     // private
13879     onLoad : function(){
13880         
13881         this.hasQuery = false;
13882         
13883         if(!this.hasFocus){
13884             return;
13885         }
13886         
13887         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13888             this.loading.hide();
13889         }
13890         
13891         if(this.store.getCount() > 0){
13892             
13893             this.expand();
13894             this.restrictHeight();
13895             if(this.lastQuery == this.allQuery){
13896                 if(this.editable && !this.tickable){
13897                     this.inputEl().dom.select();
13898                 }
13899                 
13900                 if(
13901                     !this.selectByValue(this.value, true) &&
13902                     this.autoFocus && 
13903                     (
13904                         !this.store.lastOptions ||
13905                         typeof(this.store.lastOptions.add) == 'undefined' || 
13906                         this.store.lastOptions.add != true
13907                     )
13908                 ){
13909                     this.select(0, true);
13910                 }
13911             }else{
13912                 if(this.autoFocus){
13913                     this.selectNext();
13914                 }
13915                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13916                     this.taTask.delay(this.typeAheadDelay);
13917                 }
13918             }
13919         }else{
13920             this.onEmptyResults();
13921         }
13922         
13923         //this.el.focus();
13924     },
13925     // private
13926     onLoadException : function()
13927     {
13928         this.hasQuery = false;
13929         
13930         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13931             this.loading.hide();
13932         }
13933         
13934         if(this.tickable && this.editable){
13935             return;
13936         }
13937         
13938         this.collapse();
13939         // only causes errors at present
13940         //Roo.log(this.store.reader.jsonData);
13941         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13942             // fixme
13943             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13944         //}
13945         
13946         
13947     },
13948     // private
13949     onTypeAhead : function(){
13950         if(this.store.getCount() > 0){
13951             var r = this.store.getAt(0);
13952             var newValue = r.data[this.displayField];
13953             var len = newValue.length;
13954             var selStart = this.getRawValue().length;
13955             
13956             if(selStart != len){
13957                 this.setRawValue(newValue);
13958                 this.selectText(selStart, newValue.length);
13959             }
13960         }
13961     },
13962
13963     // private
13964     onSelect : function(record, index){
13965         
13966         if(this.fireEvent('beforeselect', this, record, index) !== false){
13967         
13968             this.setFromData(index > -1 ? record.data : false);
13969             
13970             this.collapse();
13971             this.fireEvent('select', this, record, index);
13972         }
13973     },
13974
13975     /**
13976      * Returns the currently selected field value or empty string if no value is set.
13977      * @return {String} value The selected value
13978      */
13979     getValue : function()
13980     {
13981         if(Roo.isIOS && this.useNativeIOS){
13982             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13983         }
13984         
13985         if(this.multiple){
13986             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13987         }
13988         
13989         if(this.valueField){
13990             return typeof this.value != 'undefined' ? this.value : '';
13991         }else{
13992             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13993         }
13994     },
13995     
13996     getRawValue : function()
13997     {
13998         if(Roo.isIOS && this.useNativeIOS){
13999             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14000         }
14001         
14002         var v = this.inputEl().getValue();
14003         
14004         return v;
14005     },
14006
14007     /**
14008      * Clears any text/value currently set in the field
14009      */
14010     clearValue : function(){
14011         
14012         if(this.hiddenField){
14013             this.hiddenField.dom.value = '';
14014         }
14015         this.value = '';
14016         this.setRawValue('');
14017         this.lastSelectionText = '';
14018         this.lastData = false;
14019         
14020         var close = this.closeTriggerEl();
14021         
14022         if(close){
14023             close.hide();
14024         }
14025         
14026         this.validate();
14027         
14028     },
14029
14030     /**
14031      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14032      * will be displayed in the field.  If the value does not match the data value of an existing item,
14033      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14034      * Otherwise the field will be blank (although the value will still be set).
14035      * @param {String} value The value to match
14036      */
14037     setValue : function(v)
14038     {
14039         if(Roo.isIOS && this.useNativeIOS){
14040             this.setIOSValue(v);
14041             return;
14042         }
14043         
14044         if(this.multiple){
14045             this.syncValue();
14046             return;
14047         }
14048         
14049         var text = v;
14050         if(this.valueField){
14051             var r = this.findRecord(this.valueField, v);
14052             if(r){
14053                 text = r.data[this.displayField];
14054             }else if(this.valueNotFoundText !== undefined){
14055                 text = this.valueNotFoundText;
14056             }
14057         }
14058         this.lastSelectionText = text;
14059         if(this.hiddenField){
14060             this.hiddenField.dom.value = v;
14061         }
14062         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14063         this.value = v;
14064         
14065         var close = this.closeTriggerEl();
14066         
14067         if(close){
14068             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14069         }
14070         
14071         this.validate();
14072     },
14073     /**
14074      * @property {Object} the last set data for the element
14075      */
14076     
14077     lastData : false,
14078     /**
14079      * Sets the value of the field based on a object which is related to the record format for the store.
14080      * @param {Object} value the value to set as. or false on reset?
14081      */
14082     setFromData : function(o){
14083         
14084         if(this.multiple){
14085             this.addItem(o);
14086             return;
14087         }
14088             
14089         var dv = ''; // display value
14090         var vv = ''; // value value..
14091         this.lastData = o;
14092         if (this.displayField) {
14093             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14094         } else {
14095             // this is an error condition!!!
14096             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14097         }
14098         
14099         if(this.valueField){
14100             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14101         }
14102         
14103         var close = this.closeTriggerEl();
14104         
14105         if(close){
14106             if(dv.length || vv * 1 > 0){
14107                 close.show() ;
14108                 this.blockFocus=true;
14109             } else {
14110                 close.hide();
14111             }             
14112         }
14113         
14114         if(this.hiddenField){
14115             this.hiddenField.dom.value = vv;
14116             
14117             this.lastSelectionText = dv;
14118             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14119             this.value = vv;
14120             return;
14121         }
14122         // no hidden field.. - we store the value in 'value', but still display
14123         // display field!!!!
14124         this.lastSelectionText = dv;
14125         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14126         this.value = vv;
14127         
14128         
14129         
14130     },
14131     // private
14132     reset : function(){
14133         // overridden so that last data is reset..
14134         
14135         if(this.multiple){
14136             this.clearItem();
14137             return;
14138         }
14139         
14140         this.setValue(this.originalValue);
14141         //this.clearInvalid();
14142         this.lastData = false;
14143         if (this.view) {
14144             this.view.clearSelections();
14145         }
14146         
14147         this.validate();
14148     },
14149     // private
14150     findRecord : function(prop, value){
14151         var record;
14152         if(this.store.getCount() > 0){
14153             this.store.each(function(r){
14154                 if(r.data[prop] == value){
14155                     record = r;
14156                     return false;
14157                 }
14158                 return true;
14159             });
14160         }
14161         return record;
14162     },
14163     
14164     getName: function()
14165     {
14166         // returns hidden if it's set..
14167         if (!this.rendered) {return ''};
14168         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14169         
14170     },
14171     // private
14172     onViewMove : function(e, t){
14173         this.inKeyMode = false;
14174     },
14175
14176     // private
14177     onViewOver : function(e, t){
14178         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14179             return;
14180         }
14181         var item = this.view.findItemFromChild(t);
14182         
14183         if(item){
14184             var index = this.view.indexOf(item);
14185             this.select(index, false);
14186         }
14187     },
14188
14189     // private
14190     onViewClick : function(view, doFocus, el, e)
14191     {
14192         var index = this.view.getSelectedIndexes()[0];
14193         
14194         var r = this.store.getAt(index);
14195         
14196         if(this.tickable){
14197             
14198             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14199                 return;
14200             }
14201             
14202             var rm = false;
14203             var _this = this;
14204             
14205             Roo.each(this.tickItems, function(v,k){
14206                 
14207                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14208                     Roo.log(v);
14209                     _this.tickItems.splice(k, 1);
14210                     
14211                     if(typeof(e) == 'undefined' && view == false){
14212                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14213                     }
14214                     
14215                     rm = true;
14216                     return;
14217                 }
14218             });
14219             
14220             if(rm){
14221                 return;
14222             }
14223             
14224             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14225                 this.tickItems.push(r.data);
14226             }
14227             
14228             if(typeof(e) == 'undefined' && view == false){
14229                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14230             }
14231                     
14232             return;
14233         }
14234         
14235         if(r){
14236             this.onSelect(r, index);
14237         }
14238         if(doFocus !== false && !this.blockFocus){
14239             this.inputEl().focus();
14240         }
14241     },
14242
14243     // private
14244     restrictHeight : function(){
14245         //this.innerList.dom.style.height = '';
14246         //var inner = this.innerList.dom;
14247         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14248         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14249         //this.list.beginUpdate();
14250         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14251         this.list.alignTo(this.inputEl(), this.listAlign);
14252         this.list.alignTo(this.inputEl(), this.listAlign);
14253         //this.list.endUpdate();
14254     },
14255
14256     // private
14257     onEmptyResults : function(){
14258         
14259         if(this.tickable && this.editable){
14260             this.hasFocus = false;
14261             this.restrictHeight();
14262             return;
14263         }
14264         
14265         this.collapse();
14266     },
14267
14268     /**
14269      * Returns true if the dropdown list is expanded, else false.
14270      */
14271     isExpanded : function(){
14272         return this.list.isVisible();
14273     },
14274
14275     /**
14276      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14277      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14278      * @param {String} value The data value of the item to select
14279      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14280      * selected item if it is not currently in view (defaults to true)
14281      * @return {Boolean} True if the value matched an item in the list, else false
14282      */
14283     selectByValue : function(v, scrollIntoView){
14284         if(v !== undefined && v !== null){
14285             var r = this.findRecord(this.valueField || this.displayField, v);
14286             if(r){
14287                 this.select(this.store.indexOf(r), scrollIntoView);
14288                 return true;
14289             }
14290         }
14291         return false;
14292     },
14293
14294     /**
14295      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14296      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14297      * @param {Number} index The zero-based index of the list item to select
14298      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14299      * selected item if it is not currently in view (defaults to true)
14300      */
14301     select : function(index, scrollIntoView){
14302         this.selectedIndex = index;
14303         this.view.select(index);
14304         if(scrollIntoView !== false){
14305             var el = this.view.getNode(index);
14306             /*
14307              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14308              */
14309             if(el){
14310                 this.list.scrollChildIntoView(el, false);
14311             }
14312         }
14313     },
14314
14315     // private
14316     selectNext : function(){
14317         var ct = this.store.getCount();
14318         if(ct > 0){
14319             if(this.selectedIndex == -1){
14320                 this.select(0);
14321             }else if(this.selectedIndex < ct-1){
14322                 this.select(this.selectedIndex+1);
14323             }
14324         }
14325     },
14326
14327     // private
14328     selectPrev : function(){
14329         var ct = this.store.getCount();
14330         if(ct > 0){
14331             if(this.selectedIndex == -1){
14332                 this.select(0);
14333             }else if(this.selectedIndex != 0){
14334                 this.select(this.selectedIndex-1);
14335             }
14336         }
14337     },
14338
14339     // private
14340     onKeyUp : function(e){
14341         if(this.editable !== false && !e.isSpecialKey()){
14342             this.lastKey = e.getKey();
14343             this.dqTask.delay(this.queryDelay);
14344         }
14345     },
14346
14347     // private
14348     validateBlur : function(){
14349         return !this.list || !this.list.isVisible();   
14350     },
14351
14352     // private
14353     initQuery : function(){
14354         
14355         var v = this.getRawValue();
14356         
14357         if(this.tickable && this.editable){
14358             v = this.tickableInputEl().getValue();
14359         }
14360         
14361         this.doQuery(v);
14362     },
14363
14364     // private
14365     doForce : function(){
14366         if(this.inputEl().dom.value.length > 0){
14367             this.inputEl().dom.value =
14368                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14369              
14370         }
14371     },
14372
14373     /**
14374      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14375      * query allowing the query action to be canceled if needed.
14376      * @param {String} query The SQL query to execute
14377      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14378      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14379      * saved in the current store (defaults to false)
14380      */
14381     doQuery : function(q, forceAll){
14382         
14383         if(q === undefined || q === null){
14384             q = '';
14385         }
14386         var qe = {
14387             query: q,
14388             forceAll: forceAll,
14389             combo: this,
14390             cancel:false
14391         };
14392         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14393             return false;
14394         }
14395         q = qe.query;
14396         
14397         forceAll = qe.forceAll;
14398         if(forceAll === true || (q.length >= this.minChars)){
14399             
14400             this.hasQuery = true;
14401             
14402             if(this.lastQuery != q || this.alwaysQuery){
14403                 this.lastQuery = q;
14404                 if(this.mode == 'local'){
14405                     this.selectedIndex = -1;
14406                     if(forceAll){
14407                         this.store.clearFilter();
14408                     }else{
14409                         
14410                         if(this.specialFilter){
14411                             this.fireEvent('specialfilter', this);
14412                             this.onLoad();
14413                             return;
14414                         }
14415                         
14416                         this.store.filter(this.displayField, q);
14417                     }
14418                     
14419                     this.store.fireEvent("datachanged", this.store);
14420                     
14421                     this.onLoad();
14422                     
14423                     
14424                 }else{
14425                     
14426                     this.store.baseParams[this.queryParam] = q;
14427                     
14428                     var options = {params : this.getParams(q)};
14429                     
14430                     if(this.loadNext){
14431                         options.add = true;
14432                         options.params.start = this.page * this.pageSize;
14433                     }
14434                     
14435                     this.store.load(options);
14436                     
14437                     /*
14438                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14439                      *  we should expand the list on onLoad
14440                      *  so command out it
14441                      */
14442 //                    this.expand();
14443                 }
14444             }else{
14445                 this.selectedIndex = -1;
14446                 this.onLoad();   
14447             }
14448         }
14449         
14450         this.loadNext = false;
14451     },
14452     
14453     // private
14454     getParams : function(q){
14455         var p = {};
14456         //p[this.queryParam] = q;
14457         
14458         if(this.pageSize){
14459             p.start = 0;
14460             p.limit = this.pageSize;
14461         }
14462         return p;
14463     },
14464
14465     /**
14466      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14467      */
14468     collapse : function(){
14469         if(!this.isExpanded()){
14470             return;
14471         }
14472         
14473         this.list.hide();
14474         
14475         this.hasFocus = false;
14476         
14477         if(this.tickable){
14478             this.okBtn.hide();
14479             this.cancelBtn.hide();
14480             this.trigger.show();
14481             
14482             if(this.editable){
14483                 this.tickableInputEl().dom.value = '';
14484                 this.tickableInputEl().blur();
14485             }
14486             
14487         }
14488         
14489         Roo.get(document).un('mousedown', this.collapseIf, this);
14490         Roo.get(document).un('mousewheel', this.collapseIf, this);
14491         if (!this.editable) {
14492             Roo.get(document).un('keydown', this.listKeyPress, this);
14493         }
14494         this.fireEvent('collapse', this);
14495         
14496         this.validate();
14497     },
14498
14499     // private
14500     collapseIf : function(e){
14501         var in_combo  = e.within(this.el);
14502         var in_list =  e.within(this.list);
14503         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14504         
14505         if (in_combo || in_list || is_list) {
14506             //e.stopPropagation();
14507             return;
14508         }
14509         
14510         if(this.tickable){
14511             this.onTickableFooterButtonClick(e, false, false);
14512         }
14513
14514         this.collapse();
14515         
14516     },
14517
14518     /**
14519      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14520      */
14521     expand : function(){
14522        
14523         if(this.isExpanded() || !this.hasFocus){
14524             return;
14525         }
14526         
14527         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14528         this.list.setWidth(lw);
14529         
14530         Roo.log('expand');
14531         
14532         this.list.show();
14533         
14534         this.restrictHeight();
14535         
14536         if(this.tickable){
14537             
14538             this.tickItems = Roo.apply([], this.item);
14539             
14540             this.okBtn.show();
14541             this.cancelBtn.show();
14542             this.trigger.hide();
14543             
14544             if(this.editable){
14545                 this.tickableInputEl().focus();
14546             }
14547             
14548         }
14549         
14550         Roo.get(document).on('mousedown', this.collapseIf, this);
14551         Roo.get(document).on('mousewheel', this.collapseIf, this);
14552         if (!this.editable) {
14553             Roo.get(document).on('keydown', this.listKeyPress, this);
14554         }
14555         
14556         this.fireEvent('expand', this);
14557     },
14558
14559     // private
14560     // Implements the default empty TriggerField.onTriggerClick function
14561     onTriggerClick : function(e)
14562     {
14563         Roo.log('trigger click');
14564         
14565         if(this.disabled || !this.triggerList){
14566             return;
14567         }
14568         
14569         this.page = 0;
14570         this.loadNext = false;
14571         
14572         if(this.isExpanded()){
14573             this.collapse();
14574             if (!this.blockFocus) {
14575                 this.inputEl().focus();
14576             }
14577             
14578         }else {
14579             this.hasFocus = true;
14580             if(this.triggerAction == 'all') {
14581                 this.doQuery(this.allQuery, true);
14582             } else {
14583                 this.doQuery(this.getRawValue());
14584             }
14585             if (!this.blockFocus) {
14586                 this.inputEl().focus();
14587             }
14588         }
14589     },
14590     
14591     onTickableTriggerClick : function(e)
14592     {
14593         if(this.disabled){
14594             return;
14595         }
14596         
14597         this.page = 0;
14598         this.loadNext = false;
14599         this.hasFocus = true;
14600         
14601         if(this.triggerAction == 'all') {
14602             this.doQuery(this.allQuery, true);
14603         } else {
14604             this.doQuery(this.getRawValue());
14605         }
14606     },
14607     
14608     onSearchFieldClick : function(e)
14609     {
14610         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14611             this.onTickableFooterButtonClick(e, false, false);
14612             return;
14613         }
14614         
14615         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14616             return;
14617         }
14618         
14619         this.page = 0;
14620         this.loadNext = false;
14621         this.hasFocus = true;
14622         
14623         if(this.triggerAction == 'all') {
14624             this.doQuery(this.allQuery, true);
14625         } else {
14626             this.doQuery(this.getRawValue());
14627         }
14628     },
14629     
14630     listKeyPress : function(e)
14631     {
14632         //Roo.log('listkeypress');
14633         // scroll to first matching element based on key pres..
14634         if (e.isSpecialKey()) {
14635             return false;
14636         }
14637         var k = String.fromCharCode(e.getKey()).toUpperCase();
14638         //Roo.log(k);
14639         var match  = false;
14640         var csel = this.view.getSelectedNodes();
14641         var cselitem = false;
14642         if (csel.length) {
14643             var ix = this.view.indexOf(csel[0]);
14644             cselitem  = this.store.getAt(ix);
14645             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14646                 cselitem = false;
14647             }
14648             
14649         }
14650         
14651         this.store.each(function(v) { 
14652             if (cselitem) {
14653                 // start at existing selection.
14654                 if (cselitem.id == v.id) {
14655                     cselitem = false;
14656                 }
14657                 return true;
14658             }
14659                 
14660             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14661                 match = this.store.indexOf(v);
14662                 return false;
14663             }
14664             return true;
14665         }, this);
14666         
14667         if (match === false) {
14668             return true; // no more action?
14669         }
14670         // scroll to?
14671         this.view.select(match);
14672         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14673         sn.scrollIntoView(sn.dom.parentNode, false);
14674     },
14675     
14676     onViewScroll : function(e, t){
14677         
14678         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){
14679             return;
14680         }
14681         
14682         this.hasQuery = true;
14683         
14684         this.loading = this.list.select('.loading', true).first();
14685         
14686         if(this.loading === null){
14687             this.list.createChild({
14688                 tag: 'div',
14689                 cls: 'loading roo-select2-more-results roo-select2-active',
14690                 html: 'Loading more results...'
14691             });
14692             
14693             this.loading = this.list.select('.loading', true).first();
14694             
14695             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14696             
14697             this.loading.hide();
14698         }
14699         
14700         this.loading.show();
14701         
14702         var _combo = this;
14703         
14704         this.page++;
14705         this.loadNext = true;
14706         
14707         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14708         
14709         return;
14710     },
14711     
14712     addItem : function(o)
14713     {   
14714         var dv = ''; // display value
14715         
14716         if (this.displayField) {
14717             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14718         } else {
14719             // this is an error condition!!!
14720             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14721         }
14722         
14723         if(!dv.length){
14724             return;
14725         }
14726         
14727         var choice = this.choices.createChild({
14728             tag: 'li',
14729             cls: 'roo-select2-search-choice',
14730             cn: [
14731                 {
14732                     tag: 'div',
14733                     html: dv
14734                 },
14735                 {
14736                     tag: 'a',
14737                     href: '#',
14738                     cls: 'roo-select2-search-choice-close fa fa-times',
14739                     tabindex: '-1'
14740                 }
14741             ]
14742             
14743         }, this.searchField);
14744         
14745         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14746         
14747         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14748         
14749         this.item.push(o);
14750         
14751         this.lastData = o;
14752         
14753         this.syncValue();
14754         
14755         this.inputEl().dom.value = '';
14756         
14757         this.validate();
14758     },
14759     
14760     onRemoveItem : function(e, _self, o)
14761     {
14762         e.preventDefault();
14763         
14764         this.lastItem = Roo.apply([], this.item);
14765         
14766         var index = this.item.indexOf(o.data) * 1;
14767         
14768         if( index < 0){
14769             Roo.log('not this item?!');
14770             return;
14771         }
14772         
14773         this.item.splice(index, 1);
14774         o.item.remove();
14775         
14776         this.syncValue();
14777         
14778         this.fireEvent('remove', this, e);
14779         
14780         this.validate();
14781         
14782     },
14783     
14784     syncValue : function()
14785     {
14786         if(!this.item.length){
14787             this.clearValue();
14788             return;
14789         }
14790             
14791         var value = [];
14792         var _this = this;
14793         Roo.each(this.item, function(i){
14794             if(_this.valueField){
14795                 value.push(i[_this.valueField]);
14796                 return;
14797             }
14798
14799             value.push(i);
14800         });
14801
14802         this.value = value.join(',');
14803
14804         if(this.hiddenField){
14805             this.hiddenField.dom.value = this.value;
14806         }
14807         
14808         this.store.fireEvent("datachanged", this.store);
14809         
14810         this.validate();
14811     },
14812     
14813     clearItem : function()
14814     {
14815         if(!this.multiple){
14816             return;
14817         }
14818         
14819         this.item = [];
14820         
14821         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14822            c.remove();
14823         });
14824         
14825         this.syncValue();
14826         
14827         this.validate();
14828         
14829         if(this.tickable && !Roo.isTouch){
14830             this.view.refresh();
14831         }
14832     },
14833     
14834     inputEl: function ()
14835     {
14836         if(Roo.isIOS && this.useNativeIOS){
14837             return this.el.select('select.roo-ios-select', true).first();
14838         }
14839         
14840         if(Roo.isTouch && this.mobileTouchView){
14841             return this.el.select('input.form-control',true).first();
14842         }
14843         
14844         if(this.tickable){
14845             return this.searchField;
14846         }
14847         
14848         return this.el.select('input.form-control',true).first();
14849     },
14850     
14851     onTickableFooterButtonClick : function(e, btn, el)
14852     {
14853         e.preventDefault();
14854         
14855         this.lastItem = Roo.apply([], this.item);
14856         
14857         if(btn && btn.name == 'cancel'){
14858             this.tickItems = Roo.apply([], this.item);
14859             this.collapse();
14860             return;
14861         }
14862         
14863         this.clearItem();
14864         
14865         var _this = this;
14866         
14867         Roo.each(this.tickItems, function(o){
14868             _this.addItem(o);
14869         });
14870         
14871         this.collapse();
14872         
14873     },
14874     
14875     validate : function()
14876     {
14877         if(this.getVisibilityEl().hasClass('hidden')){
14878             return true;
14879         }
14880         
14881         var v = this.getRawValue();
14882         
14883         if(this.multiple){
14884             v = this.getValue();
14885         }
14886         
14887         if(this.disabled || this.allowBlank || v.length){
14888             this.markValid();
14889             return true;
14890         }
14891         
14892         this.markInvalid();
14893         return false;
14894     },
14895     
14896     tickableInputEl : function()
14897     {
14898         if(!this.tickable || !this.editable){
14899             return this.inputEl();
14900         }
14901         
14902         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14903     },
14904     
14905     
14906     getAutoCreateTouchView : function()
14907     {
14908         var id = Roo.id();
14909         
14910         var cfg = {
14911             cls: 'form-group' //input-group
14912         };
14913         
14914         var input =  {
14915             tag: 'input',
14916             id : id,
14917             type : this.inputType,
14918             cls : 'form-control x-combo-noedit',
14919             autocomplete: 'new-password',
14920             placeholder : this.placeholder || '',
14921             readonly : true
14922         };
14923         
14924         if (this.name) {
14925             input.name = this.name;
14926         }
14927         
14928         if (this.size) {
14929             input.cls += ' input-' + this.size;
14930         }
14931         
14932         if (this.disabled) {
14933             input.disabled = true;
14934         }
14935         
14936         var inputblock = {
14937             cls : '',
14938             cn : [
14939                 input
14940             ]
14941         };
14942         
14943         if(this.before){
14944             inputblock.cls += ' input-group';
14945             
14946             inputblock.cn.unshift({
14947                 tag :'span',
14948                 cls : 'input-group-addon',
14949                 html : this.before
14950             });
14951         }
14952         
14953         if(this.removable && !this.multiple){
14954             inputblock.cls += ' roo-removable';
14955             
14956             inputblock.cn.push({
14957                 tag: 'button',
14958                 html : 'x',
14959                 cls : 'roo-combo-removable-btn close'
14960             });
14961         }
14962
14963         if(this.hasFeedback && !this.allowBlank){
14964             
14965             inputblock.cls += ' has-feedback';
14966             
14967             inputblock.cn.push({
14968                 tag: 'span',
14969                 cls: 'glyphicon form-control-feedback'
14970             });
14971             
14972         }
14973         
14974         if (this.after) {
14975             
14976             inputblock.cls += (this.before) ? '' : ' input-group';
14977             
14978             inputblock.cn.push({
14979                 tag :'span',
14980                 cls : 'input-group-addon',
14981                 html : this.after
14982             });
14983         }
14984
14985         var box = {
14986             tag: 'div',
14987             cn: [
14988                 {
14989                     tag: 'input',
14990                     type : 'hidden',
14991                     cls: 'form-hidden-field'
14992                 },
14993                 inputblock
14994             ]
14995             
14996         };
14997         
14998         if(this.multiple){
14999             box = {
15000                 tag: 'div',
15001                 cn: [
15002                     {
15003                         tag: 'input',
15004                         type : 'hidden',
15005                         cls: 'form-hidden-field'
15006                     },
15007                     {
15008                         tag: 'ul',
15009                         cls: 'roo-select2-choices',
15010                         cn:[
15011                             {
15012                                 tag: 'li',
15013                                 cls: 'roo-select2-search-field',
15014                                 cn: [
15015
15016                                     inputblock
15017                                 ]
15018                             }
15019                         ]
15020                     }
15021                 ]
15022             }
15023         };
15024         
15025         var combobox = {
15026             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15027             cn: [
15028                 box
15029             ]
15030         };
15031         
15032         if(!this.multiple && this.showToggleBtn){
15033             
15034             var caret = {
15035                         tag: 'span',
15036                         cls: 'caret'
15037             };
15038             
15039             if (this.caret != false) {
15040                 caret = {
15041                      tag: 'i',
15042                      cls: 'fa fa-' + this.caret
15043                 };
15044                 
15045             }
15046             
15047             combobox.cn.push({
15048                 tag :'span',
15049                 cls : 'input-group-addon btn dropdown-toggle',
15050                 cn : [
15051                     caret,
15052                     {
15053                         tag: 'span',
15054                         cls: 'combobox-clear',
15055                         cn  : [
15056                             {
15057                                 tag : 'i',
15058                                 cls: 'icon-remove'
15059                             }
15060                         ]
15061                     }
15062                 ]
15063
15064             })
15065         }
15066         
15067         if(this.multiple){
15068             combobox.cls += ' roo-select2-container-multi';
15069         }
15070         
15071         var align = this.labelAlign || this.parentLabelAlign();
15072         
15073         if (align ==='left' && this.fieldLabel.length) {
15074
15075             cfg.cn = [
15076                 {
15077                    tag : 'i',
15078                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15079                    tooltip : 'This field is required'
15080                 },
15081                 {
15082                     tag: 'label',
15083                     cls : 'control-label',
15084                     html : this.fieldLabel
15085
15086                 },
15087                 {
15088                     cls : '', 
15089                     cn: [
15090                         combobox
15091                     ]
15092                 }
15093             ];
15094             
15095             var labelCfg = cfg.cn[1];
15096             var contentCfg = cfg.cn[2];
15097             
15098
15099             if(this.indicatorpos == 'right'){
15100                 cfg.cn = [
15101                     {
15102                         tag: 'label',
15103                         'for' :  id,
15104                         cls : 'control-label',
15105                         cn : [
15106                             {
15107                                 tag : 'span',
15108                                 html : this.fieldLabel
15109                             },
15110                             {
15111                                 tag : 'i',
15112                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15113                                 tooltip : 'This field is required'
15114                             }
15115                         ]
15116                     },
15117                     {
15118                         cls : "",
15119                         cn: [
15120                             combobox
15121                         ]
15122                     }
15123
15124                 ];
15125                 
15126                 labelCfg = cfg.cn[0];
15127                 contentCfg = cfg.cn[1];
15128             }
15129             
15130            
15131             
15132             if(this.labelWidth > 12){
15133                 labelCfg.style = "width: " + this.labelWidth + 'px';
15134             }
15135             
15136             if(this.labelWidth < 13 && this.labelmd == 0){
15137                 this.labelmd = this.labelWidth;
15138             }
15139             
15140             if(this.labellg > 0){
15141                 labelCfg.cls += ' col-lg-' + this.labellg;
15142                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15143             }
15144             
15145             if(this.labelmd > 0){
15146                 labelCfg.cls += ' col-md-' + this.labelmd;
15147                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15148             }
15149             
15150             if(this.labelsm > 0){
15151                 labelCfg.cls += ' col-sm-' + this.labelsm;
15152                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15153             }
15154             
15155             if(this.labelxs > 0){
15156                 labelCfg.cls += ' col-xs-' + this.labelxs;
15157                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15158             }
15159                 
15160                 
15161         } else if ( this.fieldLabel.length) {
15162             cfg.cn = [
15163                 {
15164                    tag : 'i',
15165                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15166                    tooltip : 'This field is required'
15167                 },
15168                 {
15169                     tag: 'label',
15170                     cls : 'control-label',
15171                     html : this.fieldLabel
15172
15173                 },
15174                 {
15175                     cls : '', 
15176                     cn: [
15177                         combobox
15178                     ]
15179                 }
15180             ];
15181             
15182             if(this.indicatorpos == 'right'){
15183                 cfg.cn = [
15184                     {
15185                         tag: 'label',
15186                         cls : 'control-label',
15187                         html : this.fieldLabel,
15188                         cn : [
15189                             {
15190                                tag : 'i',
15191                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15192                                tooltip : 'This field is required'
15193                             }
15194                         ]
15195                     },
15196                     {
15197                         cls : '', 
15198                         cn: [
15199                             combobox
15200                         ]
15201                     }
15202                 ];
15203             }
15204         } else {
15205             cfg.cn = combobox;    
15206         }
15207         
15208         
15209         var settings = this;
15210         
15211         ['xs','sm','md','lg'].map(function(size){
15212             if (settings[size]) {
15213                 cfg.cls += ' col-' + size + '-' + settings[size];
15214             }
15215         });
15216         
15217         return cfg;
15218     },
15219     
15220     initTouchView : function()
15221     {
15222         this.renderTouchView();
15223         
15224         this.touchViewEl.on('scroll', function(){
15225             this.el.dom.scrollTop = 0;
15226         }, this);
15227         
15228         this.originalValue = this.getValue();
15229         
15230         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15231         
15232         this.inputEl().on("click", this.showTouchView, this);
15233         if (this.triggerEl) {
15234             this.triggerEl.on("click", this.showTouchView, this);
15235         }
15236         
15237         
15238         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15239         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15240         
15241         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15242         
15243         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15244         this.store.on('load', this.onTouchViewLoad, this);
15245         this.store.on('loadexception', this.onTouchViewLoadException, this);
15246         
15247         if(this.hiddenName){
15248             
15249             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15250             
15251             this.hiddenField.dom.value =
15252                 this.hiddenValue !== undefined ? this.hiddenValue :
15253                 this.value !== undefined ? this.value : '';
15254         
15255             this.el.dom.removeAttribute('name');
15256             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15257         }
15258         
15259         if(this.multiple){
15260             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15261             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15262         }
15263         
15264         if(this.removable && !this.multiple){
15265             var close = this.closeTriggerEl();
15266             if(close){
15267                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15268                 close.on('click', this.removeBtnClick, this, close);
15269             }
15270         }
15271         /*
15272          * fix the bug in Safari iOS8
15273          */
15274         this.inputEl().on("focus", function(e){
15275             document.activeElement.blur();
15276         }, this);
15277         
15278         return;
15279         
15280         
15281     },
15282     
15283     renderTouchView : function()
15284     {
15285         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15286         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15287         
15288         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15289         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15290         
15291         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15292         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15293         this.touchViewBodyEl.setStyle('overflow', 'auto');
15294         
15295         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15296         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15297         
15298         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15299         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15300         
15301     },
15302     
15303     showTouchView : function()
15304     {
15305         if(this.disabled){
15306             return;
15307         }
15308         
15309         this.touchViewHeaderEl.hide();
15310
15311         if(this.modalTitle.length){
15312             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15313             this.touchViewHeaderEl.show();
15314         }
15315
15316         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15317         this.touchViewEl.show();
15318
15319         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15320         
15321         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15322         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15323
15324         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15325
15326         if(this.modalTitle.length){
15327             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15328         }
15329         
15330         this.touchViewBodyEl.setHeight(bodyHeight);
15331
15332         if(this.animate){
15333             var _this = this;
15334             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15335         }else{
15336             this.touchViewEl.addClass('in');
15337         }
15338
15339         this.doTouchViewQuery();
15340         
15341     },
15342     
15343     hideTouchView : function()
15344     {
15345         this.touchViewEl.removeClass('in');
15346
15347         if(this.animate){
15348             var _this = this;
15349             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15350         }else{
15351             this.touchViewEl.setStyle('display', 'none');
15352         }
15353         
15354     },
15355     
15356     setTouchViewValue : function()
15357     {
15358         if(this.multiple){
15359             this.clearItem();
15360         
15361             var _this = this;
15362
15363             Roo.each(this.tickItems, function(o){
15364                 this.addItem(o);
15365             }, this);
15366         }
15367         
15368         this.hideTouchView();
15369     },
15370     
15371     doTouchViewQuery : function()
15372     {
15373         var qe = {
15374             query: '',
15375             forceAll: true,
15376             combo: this,
15377             cancel:false
15378         };
15379         
15380         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15381             return false;
15382         }
15383         
15384         if(!this.alwaysQuery || this.mode == 'local'){
15385             this.onTouchViewLoad();
15386             return;
15387         }
15388         
15389         this.store.load();
15390     },
15391     
15392     onTouchViewBeforeLoad : function(combo,opts)
15393     {
15394         return;
15395     },
15396
15397     // private
15398     onTouchViewLoad : function()
15399     {
15400         if(this.store.getCount() < 1){
15401             this.onTouchViewEmptyResults();
15402             return;
15403         }
15404         
15405         this.clearTouchView();
15406         
15407         var rawValue = this.getRawValue();
15408         
15409         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15410         
15411         this.tickItems = [];
15412         
15413         this.store.data.each(function(d, rowIndex){
15414             var row = this.touchViewListGroup.createChild(template);
15415             
15416             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15417                 row.addClass(d.data.cls);
15418             }
15419             
15420             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15421                 var cfg = {
15422                     data : d.data,
15423                     html : d.data[this.displayField]
15424                 };
15425                 
15426                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15427                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15428                 }
15429             }
15430             row.removeClass('selected');
15431             if(!this.multiple && this.valueField &&
15432                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15433             {
15434                 // radio buttons..
15435                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15436                 row.addClass('selected');
15437             }
15438             
15439             if(this.multiple && this.valueField &&
15440                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15441             {
15442                 
15443                 // checkboxes...
15444                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15445                 this.tickItems.push(d.data);
15446             }
15447             
15448             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15449             
15450         }, this);
15451         
15452         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15453         
15454         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15455
15456         if(this.modalTitle.length){
15457             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15458         }
15459
15460         var listHeight = this.touchViewListGroup.getHeight();
15461         
15462         var _this = this;
15463         
15464         if(firstChecked && listHeight > bodyHeight){
15465             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15466         }
15467         
15468     },
15469     
15470     onTouchViewLoadException : function()
15471     {
15472         this.hideTouchView();
15473     },
15474     
15475     onTouchViewEmptyResults : function()
15476     {
15477         this.clearTouchView();
15478         
15479         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15480         
15481         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15482         
15483     },
15484     
15485     clearTouchView : function()
15486     {
15487         this.touchViewListGroup.dom.innerHTML = '';
15488     },
15489     
15490     onTouchViewClick : function(e, el, o)
15491     {
15492         e.preventDefault();
15493         
15494         var row = o.row;
15495         var rowIndex = o.rowIndex;
15496         
15497         var r = this.store.getAt(rowIndex);
15498         
15499         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15500             
15501             if(!this.multiple){
15502                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15503                     c.dom.removeAttribute('checked');
15504                 }, this);
15505
15506                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15507
15508                 this.setFromData(r.data);
15509
15510                 var close = this.closeTriggerEl();
15511
15512                 if(close){
15513                     close.show();
15514                 }
15515
15516                 this.hideTouchView();
15517
15518                 this.fireEvent('select', this, r, rowIndex);
15519
15520                 return;
15521             }
15522
15523             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15524                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15525                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15526                 return;
15527             }
15528
15529             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15530             this.addItem(r.data);
15531             this.tickItems.push(r.data);
15532         }
15533     },
15534     
15535     getAutoCreateNativeIOS : function()
15536     {
15537         var cfg = {
15538             cls: 'form-group' //input-group,
15539         };
15540         
15541         var combobox =  {
15542             tag: 'select',
15543             cls : 'roo-ios-select'
15544         };
15545         
15546         if (this.name) {
15547             combobox.name = this.name;
15548         }
15549         
15550         if (this.disabled) {
15551             combobox.disabled = true;
15552         }
15553         
15554         var settings = this;
15555         
15556         ['xs','sm','md','lg'].map(function(size){
15557             if (settings[size]) {
15558                 cfg.cls += ' col-' + size + '-' + settings[size];
15559             }
15560         });
15561         
15562         cfg.cn = combobox;
15563         
15564         return cfg;
15565         
15566     },
15567     
15568     initIOSView : function()
15569     {
15570         this.store.on('load', this.onIOSViewLoad, this);
15571         
15572         return;
15573     },
15574     
15575     onIOSViewLoad : function()
15576     {
15577         if(this.store.getCount() < 1){
15578             return;
15579         }
15580         
15581         this.clearIOSView();
15582         
15583         if(this.allowBlank) {
15584             
15585             var default_text = '-- SELECT --';
15586             
15587             if(this.placeholder.length){
15588                 default_text = this.placeholder;
15589             }
15590             
15591             if(this.emptyTitle.length){
15592                 default_text += ' - ' + this.emptyTitle + ' -';
15593             }
15594             
15595             var opt = this.inputEl().createChild({
15596                 tag: 'option',
15597                 value : 0,
15598                 html : default_text
15599             });
15600             
15601             var o = {};
15602             o[this.valueField] = 0;
15603             o[this.displayField] = default_text;
15604             
15605             this.ios_options.push({
15606                 data : o,
15607                 el : opt
15608             });
15609             
15610         }
15611         
15612         this.store.data.each(function(d, rowIndex){
15613             
15614             var html = '';
15615             
15616             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15617                 html = d.data[this.displayField];
15618             }
15619             
15620             var value = '';
15621             
15622             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15623                 value = d.data[this.valueField];
15624             }
15625             
15626             var option = {
15627                 tag: 'option',
15628                 value : value,
15629                 html : html
15630             };
15631             
15632             if(this.value == d.data[this.valueField]){
15633                 option['selected'] = true;
15634             }
15635             
15636             var opt = this.inputEl().createChild(option);
15637             
15638             this.ios_options.push({
15639                 data : d.data,
15640                 el : opt
15641             });
15642             
15643         }, this);
15644         
15645         this.inputEl().on('change', function(){
15646            this.fireEvent('select', this);
15647         }, this);
15648         
15649     },
15650     
15651     clearIOSView: function()
15652     {
15653         this.inputEl().dom.innerHTML = '';
15654         
15655         this.ios_options = [];
15656     },
15657     
15658     setIOSValue: function(v)
15659     {
15660         this.value = v;
15661         
15662         if(!this.ios_options){
15663             return;
15664         }
15665         
15666         Roo.each(this.ios_options, function(opts){
15667            
15668            opts.el.dom.removeAttribute('selected');
15669            
15670            if(opts.data[this.valueField] != v){
15671                return;
15672            }
15673            
15674            opts.el.dom.setAttribute('selected', true);
15675            
15676         }, this);
15677     }
15678
15679     /** 
15680     * @cfg {Boolean} grow 
15681     * @hide 
15682     */
15683     /** 
15684     * @cfg {Number} growMin 
15685     * @hide 
15686     */
15687     /** 
15688     * @cfg {Number} growMax 
15689     * @hide 
15690     */
15691     /**
15692      * @hide
15693      * @method autoSize
15694      */
15695 });
15696
15697 Roo.apply(Roo.bootstrap.ComboBox,  {
15698     
15699     header : {
15700         tag: 'div',
15701         cls: 'modal-header',
15702         cn: [
15703             {
15704                 tag: 'h4',
15705                 cls: 'modal-title'
15706             }
15707         ]
15708     },
15709     
15710     body : {
15711         tag: 'div',
15712         cls: 'modal-body',
15713         cn: [
15714             {
15715                 tag: 'ul',
15716                 cls: 'list-group'
15717             }
15718         ]
15719     },
15720     
15721     listItemRadio : {
15722         tag: 'li',
15723         cls: 'list-group-item',
15724         cn: [
15725             {
15726                 tag: 'span',
15727                 cls: 'roo-combobox-list-group-item-value'
15728             },
15729             {
15730                 tag: 'div',
15731                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15732                 cn: [
15733                     {
15734                         tag: 'input',
15735                         type: 'radio'
15736                     },
15737                     {
15738                         tag: 'label'
15739                     }
15740                 ]
15741             }
15742         ]
15743     },
15744     
15745     listItemCheckbox : {
15746         tag: 'li',
15747         cls: 'list-group-item',
15748         cn: [
15749             {
15750                 tag: 'span',
15751                 cls: 'roo-combobox-list-group-item-value'
15752             },
15753             {
15754                 tag: 'div',
15755                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15756                 cn: [
15757                     {
15758                         tag: 'input',
15759                         type: 'checkbox'
15760                     },
15761                     {
15762                         tag: 'label'
15763                     }
15764                 ]
15765             }
15766         ]
15767     },
15768     
15769     emptyResult : {
15770         tag: 'div',
15771         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15772     },
15773     
15774     footer : {
15775         tag: 'div',
15776         cls: 'modal-footer',
15777         cn: [
15778             {
15779                 tag: 'div',
15780                 cls: 'row',
15781                 cn: [
15782                     {
15783                         tag: 'div',
15784                         cls: 'col-xs-6 text-left',
15785                         cn: {
15786                             tag: 'button',
15787                             cls: 'btn btn-danger roo-touch-view-cancel',
15788                             html: 'Cancel'
15789                         }
15790                     },
15791                     {
15792                         tag: 'div',
15793                         cls: 'col-xs-6 text-right',
15794                         cn: {
15795                             tag: 'button',
15796                             cls: 'btn btn-success roo-touch-view-ok',
15797                             html: 'OK'
15798                         }
15799                     }
15800                 ]
15801             }
15802         ]
15803         
15804     }
15805 });
15806
15807 Roo.apply(Roo.bootstrap.ComboBox,  {
15808     
15809     touchViewTemplate : {
15810         tag: 'div',
15811         cls: 'modal fade roo-combobox-touch-view',
15812         cn: [
15813             {
15814                 tag: 'div',
15815                 cls: 'modal-dialog',
15816                 style : 'position:fixed', // we have to fix position....
15817                 cn: [
15818                     {
15819                         tag: 'div',
15820                         cls: 'modal-content',
15821                         cn: [
15822                             Roo.bootstrap.ComboBox.header,
15823                             Roo.bootstrap.ComboBox.body,
15824                             Roo.bootstrap.ComboBox.footer
15825                         ]
15826                     }
15827                 ]
15828             }
15829         ]
15830     }
15831 });/*
15832  * Based on:
15833  * Ext JS Library 1.1.1
15834  * Copyright(c) 2006-2007, Ext JS, LLC.
15835  *
15836  * Originally Released Under LGPL - original licence link has changed is not relivant.
15837  *
15838  * Fork - LGPL
15839  * <script type="text/javascript">
15840  */
15841
15842 /**
15843  * @class Roo.View
15844  * @extends Roo.util.Observable
15845  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15846  * This class also supports single and multi selection modes. <br>
15847  * Create a data model bound view:
15848  <pre><code>
15849  var store = new Roo.data.Store(...);
15850
15851  var view = new Roo.View({
15852     el : "my-element",
15853     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15854  
15855     singleSelect: true,
15856     selectedClass: "ydataview-selected",
15857     store: store
15858  });
15859
15860  // listen for node click?
15861  view.on("click", function(vw, index, node, e){
15862  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15863  });
15864
15865  // load XML data
15866  dataModel.load("foobar.xml");
15867  </code></pre>
15868  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15869  * <br><br>
15870  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15871  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15872  * 
15873  * Note: old style constructor is still suported (container, template, config)
15874  * 
15875  * @constructor
15876  * Create a new View
15877  * @param {Object} config The config object
15878  * 
15879  */
15880 Roo.View = function(config, depreciated_tpl, depreciated_config){
15881     
15882     this.parent = false;
15883     
15884     if (typeof(depreciated_tpl) == 'undefined') {
15885         // new way.. - universal constructor.
15886         Roo.apply(this, config);
15887         this.el  = Roo.get(this.el);
15888     } else {
15889         // old format..
15890         this.el  = Roo.get(config);
15891         this.tpl = depreciated_tpl;
15892         Roo.apply(this, depreciated_config);
15893     }
15894     this.wrapEl  = this.el.wrap().wrap();
15895     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15896     
15897     
15898     if(typeof(this.tpl) == "string"){
15899         this.tpl = new Roo.Template(this.tpl);
15900     } else {
15901         // support xtype ctors..
15902         this.tpl = new Roo.factory(this.tpl, Roo);
15903     }
15904     
15905     
15906     this.tpl.compile();
15907     
15908     /** @private */
15909     this.addEvents({
15910         /**
15911          * @event beforeclick
15912          * Fires before a click is processed. Returns false to cancel the default action.
15913          * @param {Roo.View} this
15914          * @param {Number} index The index of the target node
15915          * @param {HTMLElement} node The target node
15916          * @param {Roo.EventObject} e The raw event object
15917          */
15918             "beforeclick" : true,
15919         /**
15920          * @event click
15921          * Fires when a template node is clicked.
15922          * @param {Roo.View} this
15923          * @param {Number} index The index of the target node
15924          * @param {HTMLElement} node The target node
15925          * @param {Roo.EventObject} e The raw event object
15926          */
15927             "click" : true,
15928         /**
15929          * @event dblclick
15930          * Fires when a template node is double clicked.
15931          * @param {Roo.View} this
15932          * @param {Number} index The index of the target node
15933          * @param {HTMLElement} node The target node
15934          * @param {Roo.EventObject} e The raw event object
15935          */
15936             "dblclick" : true,
15937         /**
15938          * @event contextmenu
15939          * Fires when a template node is right clicked.
15940          * @param {Roo.View} this
15941          * @param {Number} index The index of the target node
15942          * @param {HTMLElement} node The target node
15943          * @param {Roo.EventObject} e The raw event object
15944          */
15945             "contextmenu" : true,
15946         /**
15947          * @event selectionchange
15948          * Fires when the selected nodes change.
15949          * @param {Roo.View} this
15950          * @param {Array} selections Array of the selected nodes
15951          */
15952             "selectionchange" : true,
15953     
15954         /**
15955          * @event beforeselect
15956          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15957          * @param {Roo.View} this
15958          * @param {HTMLElement} node The node to be selected
15959          * @param {Array} selections Array of currently selected nodes
15960          */
15961             "beforeselect" : true,
15962         /**
15963          * @event preparedata
15964          * Fires on every row to render, to allow you to change the data.
15965          * @param {Roo.View} this
15966          * @param {Object} data to be rendered (change this)
15967          */
15968           "preparedata" : true
15969           
15970           
15971         });
15972
15973
15974
15975     this.el.on({
15976         "click": this.onClick,
15977         "dblclick": this.onDblClick,
15978         "contextmenu": this.onContextMenu,
15979         scope:this
15980     });
15981
15982     this.selections = [];
15983     this.nodes = [];
15984     this.cmp = new Roo.CompositeElementLite([]);
15985     if(this.store){
15986         this.store = Roo.factory(this.store, Roo.data);
15987         this.setStore(this.store, true);
15988     }
15989     
15990     if ( this.footer && this.footer.xtype) {
15991            
15992          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15993         
15994         this.footer.dataSource = this.store;
15995         this.footer.container = fctr;
15996         this.footer = Roo.factory(this.footer, Roo);
15997         fctr.insertFirst(this.el);
15998         
15999         // this is a bit insane - as the paging toolbar seems to detach the el..
16000 //        dom.parentNode.parentNode.parentNode
16001          // they get detached?
16002     }
16003     
16004     
16005     Roo.View.superclass.constructor.call(this);
16006     
16007     
16008 };
16009
16010 Roo.extend(Roo.View, Roo.util.Observable, {
16011     
16012      /**
16013      * @cfg {Roo.data.Store} store Data store to load data from.
16014      */
16015     store : false,
16016     
16017     /**
16018      * @cfg {String|Roo.Element} el The container element.
16019      */
16020     el : '',
16021     
16022     /**
16023      * @cfg {String|Roo.Template} tpl The template used by this View 
16024      */
16025     tpl : false,
16026     /**
16027      * @cfg {String} dataName the named area of the template to use as the data area
16028      *                          Works with domtemplates roo-name="name"
16029      */
16030     dataName: false,
16031     /**
16032      * @cfg {String} selectedClass The css class to add to selected nodes
16033      */
16034     selectedClass : "x-view-selected",
16035      /**
16036      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16037      */
16038     emptyText : "",
16039     
16040     /**
16041      * @cfg {String} text to display on mask (default Loading)
16042      */
16043     mask : false,
16044     /**
16045      * @cfg {Boolean} multiSelect Allow multiple selection
16046      */
16047     multiSelect : false,
16048     /**
16049      * @cfg {Boolean} singleSelect Allow single selection
16050      */
16051     singleSelect:  false,
16052     
16053     /**
16054      * @cfg {Boolean} toggleSelect - selecting 
16055      */
16056     toggleSelect : false,
16057     
16058     /**
16059      * @cfg {Boolean} tickable - selecting 
16060      */
16061     tickable : false,
16062     
16063     /**
16064      * Returns the element this view is bound to.
16065      * @return {Roo.Element}
16066      */
16067     getEl : function(){
16068         return this.wrapEl;
16069     },
16070     
16071     
16072
16073     /**
16074      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16075      */
16076     refresh : function(){
16077         //Roo.log('refresh');
16078         var t = this.tpl;
16079         
16080         // if we are using something like 'domtemplate', then
16081         // the what gets used is:
16082         // t.applySubtemplate(NAME, data, wrapping data..)
16083         // the outer template then get' applied with
16084         //     the store 'extra data'
16085         // and the body get's added to the
16086         //      roo-name="data" node?
16087         //      <span class='roo-tpl-{name}'></span> ?????
16088         
16089         
16090         
16091         this.clearSelections();
16092         this.el.update("");
16093         var html = [];
16094         var records = this.store.getRange();
16095         if(records.length < 1) {
16096             
16097             // is this valid??  = should it render a template??
16098             
16099             this.el.update(this.emptyText);
16100             return;
16101         }
16102         var el = this.el;
16103         if (this.dataName) {
16104             this.el.update(t.apply(this.store.meta)); //????
16105             el = this.el.child('.roo-tpl-' + this.dataName);
16106         }
16107         
16108         for(var i = 0, len = records.length; i < len; i++){
16109             var data = this.prepareData(records[i].data, i, records[i]);
16110             this.fireEvent("preparedata", this, data, i, records[i]);
16111             
16112             var d = Roo.apply({}, data);
16113             
16114             if(this.tickable){
16115                 Roo.apply(d, {'roo-id' : Roo.id()});
16116                 
16117                 var _this = this;
16118             
16119                 Roo.each(this.parent.item, function(item){
16120                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16121                         return;
16122                     }
16123                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16124                 });
16125             }
16126             
16127             html[html.length] = Roo.util.Format.trim(
16128                 this.dataName ?
16129                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16130                     t.apply(d)
16131             );
16132         }
16133         
16134         
16135         
16136         el.update(html.join(""));
16137         this.nodes = el.dom.childNodes;
16138         this.updateIndexes(0);
16139     },
16140     
16141
16142     /**
16143      * Function to override to reformat the data that is sent to
16144      * the template for each node.
16145      * DEPRICATED - use the preparedata event handler.
16146      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16147      * a JSON object for an UpdateManager bound view).
16148      */
16149     prepareData : function(data, index, record)
16150     {
16151         this.fireEvent("preparedata", this, data, index, record);
16152         return data;
16153     },
16154
16155     onUpdate : function(ds, record){
16156         // Roo.log('on update');   
16157         this.clearSelections();
16158         var index = this.store.indexOf(record);
16159         var n = this.nodes[index];
16160         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16161         n.parentNode.removeChild(n);
16162         this.updateIndexes(index, index);
16163     },
16164
16165     
16166     
16167 // --------- FIXME     
16168     onAdd : function(ds, records, index)
16169     {
16170         //Roo.log(['on Add', ds, records, index] );        
16171         this.clearSelections();
16172         if(this.nodes.length == 0){
16173             this.refresh();
16174             return;
16175         }
16176         var n = this.nodes[index];
16177         for(var i = 0, len = records.length; i < len; i++){
16178             var d = this.prepareData(records[i].data, i, records[i]);
16179             if(n){
16180                 this.tpl.insertBefore(n, d);
16181             }else{
16182                 
16183                 this.tpl.append(this.el, d);
16184             }
16185         }
16186         this.updateIndexes(index);
16187     },
16188
16189     onRemove : function(ds, record, index){
16190        // Roo.log('onRemove');
16191         this.clearSelections();
16192         var el = this.dataName  ?
16193             this.el.child('.roo-tpl-' + this.dataName) :
16194             this.el; 
16195         
16196         el.dom.removeChild(this.nodes[index]);
16197         this.updateIndexes(index);
16198     },
16199
16200     /**
16201      * Refresh an individual node.
16202      * @param {Number} index
16203      */
16204     refreshNode : function(index){
16205         this.onUpdate(this.store, this.store.getAt(index));
16206     },
16207
16208     updateIndexes : function(startIndex, endIndex){
16209         var ns = this.nodes;
16210         startIndex = startIndex || 0;
16211         endIndex = endIndex || ns.length - 1;
16212         for(var i = startIndex; i <= endIndex; i++){
16213             ns[i].nodeIndex = i;
16214         }
16215     },
16216
16217     /**
16218      * Changes the data store this view uses and refresh the view.
16219      * @param {Store} store
16220      */
16221     setStore : function(store, initial){
16222         if(!initial && this.store){
16223             this.store.un("datachanged", this.refresh);
16224             this.store.un("add", this.onAdd);
16225             this.store.un("remove", this.onRemove);
16226             this.store.un("update", this.onUpdate);
16227             this.store.un("clear", this.refresh);
16228             this.store.un("beforeload", this.onBeforeLoad);
16229             this.store.un("load", this.onLoad);
16230             this.store.un("loadexception", this.onLoad);
16231         }
16232         if(store){
16233           
16234             store.on("datachanged", this.refresh, this);
16235             store.on("add", this.onAdd, this);
16236             store.on("remove", this.onRemove, this);
16237             store.on("update", this.onUpdate, this);
16238             store.on("clear", this.refresh, this);
16239             store.on("beforeload", this.onBeforeLoad, this);
16240             store.on("load", this.onLoad, this);
16241             store.on("loadexception", this.onLoad, this);
16242         }
16243         
16244         if(store){
16245             this.refresh();
16246         }
16247     },
16248     /**
16249      * onbeforeLoad - masks the loading area.
16250      *
16251      */
16252     onBeforeLoad : function(store,opts)
16253     {
16254          //Roo.log('onBeforeLoad');   
16255         if (!opts.add) {
16256             this.el.update("");
16257         }
16258         this.el.mask(this.mask ? this.mask : "Loading" ); 
16259     },
16260     onLoad : function ()
16261     {
16262         this.el.unmask();
16263     },
16264     
16265
16266     /**
16267      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16268      * @param {HTMLElement} node
16269      * @return {HTMLElement} The template node
16270      */
16271     findItemFromChild : function(node){
16272         var el = this.dataName  ?
16273             this.el.child('.roo-tpl-' + this.dataName,true) :
16274             this.el.dom; 
16275         
16276         if(!node || node.parentNode == el){
16277                     return node;
16278             }
16279             var p = node.parentNode;
16280             while(p && p != el){
16281             if(p.parentNode == el){
16282                 return p;
16283             }
16284             p = p.parentNode;
16285         }
16286             return null;
16287     },
16288
16289     /** @ignore */
16290     onClick : function(e){
16291         var item = this.findItemFromChild(e.getTarget());
16292         if(item){
16293             var index = this.indexOf(item);
16294             if(this.onItemClick(item, index, e) !== false){
16295                 this.fireEvent("click", this, index, item, e);
16296             }
16297         }else{
16298             this.clearSelections();
16299         }
16300     },
16301
16302     /** @ignore */
16303     onContextMenu : function(e){
16304         var item = this.findItemFromChild(e.getTarget());
16305         if(item){
16306             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16307         }
16308     },
16309
16310     /** @ignore */
16311     onDblClick : function(e){
16312         var item = this.findItemFromChild(e.getTarget());
16313         if(item){
16314             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16315         }
16316     },
16317
16318     onItemClick : function(item, index, e)
16319     {
16320         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16321             return false;
16322         }
16323         if (this.toggleSelect) {
16324             var m = this.isSelected(item) ? 'unselect' : 'select';
16325             //Roo.log(m);
16326             var _t = this;
16327             _t[m](item, true, false);
16328             return true;
16329         }
16330         if(this.multiSelect || this.singleSelect){
16331             if(this.multiSelect && e.shiftKey && this.lastSelection){
16332                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16333             }else{
16334                 this.select(item, this.multiSelect && e.ctrlKey);
16335                 this.lastSelection = item;
16336             }
16337             
16338             if(!this.tickable){
16339                 e.preventDefault();
16340             }
16341             
16342         }
16343         return true;
16344     },
16345
16346     /**
16347      * Get the number of selected nodes.
16348      * @return {Number}
16349      */
16350     getSelectionCount : function(){
16351         return this.selections.length;
16352     },
16353
16354     /**
16355      * Get the currently selected nodes.
16356      * @return {Array} An array of HTMLElements
16357      */
16358     getSelectedNodes : function(){
16359         return this.selections;
16360     },
16361
16362     /**
16363      * Get the indexes of the selected nodes.
16364      * @return {Array}
16365      */
16366     getSelectedIndexes : function(){
16367         var indexes = [], s = this.selections;
16368         for(var i = 0, len = s.length; i < len; i++){
16369             indexes.push(s[i].nodeIndex);
16370         }
16371         return indexes;
16372     },
16373
16374     /**
16375      * Clear all selections
16376      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16377      */
16378     clearSelections : function(suppressEvent){
16379         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16380             this.cmp.elements = this.selections;
16381             this.cmp.removeClass(this.selectedClass);
16382             this.selections = [];
16383             if(!suppressEvent){
16384                 this.fireEvent("selectionchange", this, this.selections);
16385             }
16386         }
16387     },
16388
16389     /**
16390      * Returns true if the passed node is selected
16391      * @param {HTMLElement/Number} node The node or node index
16392      * @return {Boolean}
16393      */
16394     isSelected : function(node){
16395         var s = this.selections;
16396         if(s.length < 1){
16397             return false;
16398         }
16399         node = this.getNode(node);
16400         return s.indexOf(node) !== -1;
16401     },
16402
16403     /**
16404      * Selects nodes.
16405      * @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
16406      * @param {Boolean} keepExisting (optional) true to keep existing selections
16407      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16408      */
16409     select : function(nodeInfo, keepExisting, suppressEvent){
16410         if(nodeInfo instanceof Array){
16411             if(!keepExisting){
16412                 this.clearSelections(true);
16413             }
16414             for(var i = 0, len = nodeInfo.length; i < len; i++){
16415                 this.select(nodeInfo[i], true, true);
16416             }
16417             return;
16418         } 
16419         var node = this.getNode(nodeInfo);
16420         if(!node || this.isSelected(node)){
16421             return; // already selected.
16422         }
16423         if(!keepExisting){
16424             this.clearSelections(true);
16425         }
16426         
16427         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16428             Roo.fly(node).addClass(this.selectedClass);
16429             this.selections.push(node);
16430             if(!suppressEvent){
16431                 this.fireEvent("selectionchange", this, this.selections);
16432             }
16433         }
16434         
16435         
16436     },
16437       /**
16438      * Unselects nodes.
16439      * @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
16440      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16441      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16442      */
16443     unselect : function(nodeInfo, keepExisting, suppressEvent)
16444     {
16445         if(nodeInfo instanceof Array){
16446             Roo.each(this.selections, function(s) {
16447                 this.unselect(s, nodeInfo);
16448             }, this);
16449             return;
16450         }
16451         var node = this.getNode(nodeInfo);
16452         if(!node || !this.isSelected(node)){
16453             //Roo.log("not selected");
16454             return; // not selected.
16455         }
16456         // fireevent???
16457         var ns = [];
16458         Roo.each(this.selections, function(s) {
16459             if (s == node ) {
16460                 Roo.fly(node).removeClass(this.selectedClass);
16461
16462                 return;
16463             }
16464             ns.push(s);
16465         },this);
16466         
16467         this.selections= ns;
16468         this.fireEvent("selectionchange", this, this.selections);
16469     },
16470
16471     /**
16472      * Gets a template node.
16473      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16474      * @return {HTMLElement} The node or null if it wasn't found
16475      */
16476     getNode : function(nodeInfo){
16477         if(typeof nodeInfo == "string"){
16478             return document.getElementById(nodeInfo);
16479         }else if(typeof nodeInfo == "number"){
16480             return this.nodes[nodeInfo];
16481         }
16482         return nodeInfo;
16483     },
16484
16485     /**
16486      * Gets a range template nodes.
16487      * @param {Number} startIndex
16488      * @param {Number} endIndex
16489      * @return {Array} An array of nodes
16490      */
16491     getNodes : function(start, end){
16492         var ns = this.nodes;
16493         start = start || 0;
16494         end = typeof end == "undefined" ? ns.length - 1 : end;
16495         var nodes = [];
16496         if(start <= end){
16497             for(var i = start; i <= end; i++){
16498                 nodes.push(ns[i]);
16499             }
16500         } else{
16501             for(var i = start; i >= end; i--){
16502                 nodes.push(ns[i]);
16503             }
16504         }
16505         return nodes;
16506     },
16507
16508     /**
16509      * Finds the index of the passed node
16510      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16511      * @return {Number} The index of the node or -1
16512      */
16513     indexOf : function(node){
16514         node = this.getNode(node);
16515         if(typeof node.nodeIndex == "number"){
16516             return node.nodeIndex;
16517         }
16518         var ns = this.nodes;
16519         for(var i = 0, len = ns.length; i < len; i++){
16520             if(ns[i] == node){
16521                 return i;
16522             }
16523         }
16524         return -1;
16525     }
16526 });
16527 /*
16528  * - LGPL
16529  *
16530  * based on jquery fullcalendar
16531  * 
16532  */
16533
16534 Roo.bootstrap = Roo.bootstrap || {};
16535 /**
16536  * @class Roo.bootstrap.Calendar
16537  * @extends Roo.bootstrap.Component
16538  * Bootstrap Calendar class
16539  * @cfg {Boolean} loadMask (true|false) default false
16540  * @cfg {Object} header generate the user specific header of the calendar, default false
16541
16542  * @constructor
16543  * Create a new Container
16544  * @param {Object} config The config object
16545  */
16546
16547
16548
16549 Roo.bootstrap.Calendar = function(config){
16550     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16551      this.addEvents({
16552         /**
16553              * @event select
16554              * Fires when a date is selected
16555              * @param {DatePicker} this
16556              * @param {Date} date The selected date
16557              */
16558         'select': true,
16559         /**
16560              * @event monthchange
16561              * Fires when the displayed month changes 
16562              * @param {DatePicker} this
16563              * @param {Date} date The selected month
16564              */
16565         'monthchange': true,
16566         /**
16567              * @event evententer
16568              * Fires when mouse over an event
16569              * @param {Calendar} this
16570              * @param {event} Event
16571              */
16572         'evententer': true,
16573         /**
16574              * @event eventleave
16575              * Fires when the mouse leaves an
16576              * @param {Calendar} this
16577              * @param {event}
16578              */
16579         'eventleave': true,
16580         /**
16581              * @event eventclick
16582              * Fires when the mouse click an
16583              * @param {Calendar} this
16584              * @param {event}
16585              */
16586         'eventclick': true
16587         
16588     });
16589
16590 };
16591
16592 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16593     
16594      /**
16595      * @cfg {Number} startDay
16596      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16597      */
16598     startDay : 0,
16599     
16600     loadMask : false,
16601     
16602     header : false,
16603       
16604     getAutoCreate : function(){
16605         
16606         
16607         var fc_button = function(name, corner, style, content ) {
16608             return Roo.apply({},{
16609                 tag : 'span',
16610                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16611                          (corner.length ?
16612                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16613                             ''
16614                         ),
16615                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16616                 unselectable: 'on'
16617             });
16618         };
16619         
16620         var header = {};
16621         
16622         if(!this.header){
16623             header = {
16624                 tag : 'table',
16625                 cls : 'fc-header',
16626                 style : 'width:100%',
16627                 cn : [
16628                     {
16629                         tag: 'tr',
16630                         cn : [
16631                             {
16632                                 tag : 'td',
16633                                 cls : 'fc-header-left',
16634                                 cn : [
16635                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16636                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16637                                     { tag: 'span', cls: 'fc-header-space' },
16638                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16639
16640
16641                                 ]
16642                             },
16643
16644                             {
16645                                 tag : 'td',
16646                                 cls : 'fc-header-center',
16647                                 cn : [
16648                                     {
16649                                         tag: 'span',
16650                                         cls: 'fc-header-title',
16651                                         cn : {
16652                                             tag: 'H2',
16653                                             html : 'month / year'
16654                                         }
16655                                     }
16656
16657                                 ]
16658                             },
16659                             {
16660                                 tag : 'td',
16661                                 cls : 'fc-header-right',
16662                                 cn : [
16663                               /*      fc_button('month', 'left', '', 'month' ),
16664                                     fc_button('week', '', '', 'week' ),
16665                                     fc_button('day', 'right', '', 'day' )
16666                                 */    
16667
16668                                 ]
16669                             }
16670
16671                         ]
16672                     }
16673                 ]
16674             };
16675         }
16676         
16677         header = this.header;
16678         
16679        
16680         var cal_heads = function() {
16681             var ret = [];
16682             // fixme - handle this.
16683             
16684             for (var i =0; i < Date.dayNames.length; i++) {
16685                 var d = Date.dayNames[i];
16686                 ret.push({
16687                     tag: 'th',
16688                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16689                     html : d.substring(0,3)
16690                 });
16691                 
16692             }
16693             ret[0].cls += ' fc-first';
16694             ret[6].cls += ' fc-last';
16695             return ret;
16696         };
16697         var cal_cell = function(n) {
16698             return  {
16699                 tag: 'td',
16700                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16701                 cn : [
16702                     {
16703                         cn : [
16704                             {
16705                                 cls: 'fc-day-number',
16706                                 html: 'D'
16707                             },
16708                             {
16709                                 cls: 'fc-day-content',
16710                              
16711                                 cn : [
16712                                      {
16713                                         style: 'position: relative;' // height: 17px;
16714                                     }
16715                                 ]
16716                             }
16717                             
16718                             
16719                         ]
16720                     }
16721                 ]
16722                 
16723             }
16724         };
16725         var cal_rows = function() {
16726             
16727             var ret = [];
16728             for (var r = 0; r < 6; r++) {
16729                 var row= {
16730                     tag : 'tr',
16731                     cls : 'fc-week',
16732                     cn : []
16733                 };
16734                 
16735                 for (var i =0; i < Date.dayNames.length; i++) {
16736                     var d = Date.dayNames[i];
16737                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16738
16739                 }
16740                 row.cn[0].cls+=' fc-first';
16741                 row.cn[0].cn[0].style = 'min-height:90px';
16742                 row.cn[6].cls+=' fc-last';
16743                 ret.push(row);
16744                 
16745             }
16746             ret[0].cls += ' fc-first';
16747             ret[4].cls += ' fc-prev-last';
16748             ret[5].cls += ' fc-last';
16749             return ret;
16750             
16751         };
16752         
16753         var cal_table = {
16754             tag: 'table',
16755             cls: 'fc-border-separate',
16756             style : 'width:100%',
16757             cellspacing  : 0,
16758             cn : [
16759                 { 
16760                     tag: 'thead',
16761                     cn : [
16762                         { 
16763                             tag: 'tr',
16764                             cls : 'fc-first fc-last',
16765                             cn : cal_heads()
16766                         }
16767                     ]
16768                 },
16769                 { 
16770                     tag: 'tbody',
16771                     cn : cal_rows()
16772                 }
16773                   
16774             ]
16775         };
16776          
16777          var cfg = {
16778             cls : 'fc fc-ltr',
16779             cn : [
16780                 header,
16781                 {
16782                     cls : 'fc-content',
16783                     style : "position: relative;",
16784                     cn : [
16785                         {
16786                             cls : 'fc-view fc-view-month fc-grid',
16787                             style : 'position: relative',
16788                             unselectable : 'on',
16789                             cn : [
16790                                 {
16791                                     cls : 'fc-event-container',
16792                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16793                                 },
16794                                 cal_table
16795                             ]
16796                         }
16797                     ]
16798     
16799                 }
16800            ] 
16801             
16802         };
16803         
16804          
16805         
16806         return cfg;
16807     },
16808     
16809     
16810     initEvents : function()
16811     {
16812         if(!this.store){
16813             throw "can not find store for calendar";
16814         }
16815         
16816         var mark = {
16817             tag: "div",
16818             cls:"x-dlg-mask",
16819             style: "text-align:center",
16820             cn: [
16821                 {
16822                     tag: "div",
16823                     style: "background-color:white;width:50%;margin:250 auto",
16824                     cn: [
16825                         {
16826                             tag: "img",
16827                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16828                         },
16829                         {
16830                             tag: "span",
16831                             html: "Loading"
16832                         }
16833                         
16834                     ]
16835                 }
16836             ]
16837         };
16838         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16839         
16840         var size = this.el.select('.fc-content', true).first().getSize();
16841         this.maskEl.setSize(size.width, size.height);
16842         this.maskEl.enableDisplayMode("block");
16843         if(!this.loadMask){
16844             this.maskEl.hide();
16845         }
16846         
16847         this.store = Roo.factory(this.store, Roo.data);
16848         this.store.on('load', this.onLoad, this);
16849         this.store.on('beforeload', this.onBeforeLoad, this);
16850         
16851         this.resize();
16852         
16853         this.cells = this.el.select('.fc-day',true);
16854         //Roo.log(this.cells);
16855         this.textNodes = this.el.query('.fc-day-number');
16856         this.cells.addClassOnOver('fc-state-hover');
16857         
16858         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16859         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16860         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16861         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16862         
16863         this.on('monthchange', this.onMonthChange, this);
16864         
16865         this.update(new Date().clearTime());
16866     },
16867     
16868     resize : function() {
16869         var sz  = this.el.getSize();
16870         
16871         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16872         this.el.select('.fc-day-content div',true).setHeight(34);
16873     },
16874     
16875     
16876     // private
16877     showPrevMonth : function(e){
16878         this.update(this.activeDate.add("mo", -1));
16879     },
16880     showToday : function(e){
16881         this.update(new Date().clearTime());
16882     },
16883     // private
16884     showNextMonth : function(e){
16885         this.update(this.activeDate.add("mo", 1));
16886     },
16887
16888     // private
16889     showPrevYear : function(){
16890         this.update(this.activeDate.add("y", -1));
16891     },
16892
16893     // private
16894     showNextYear : function(){
16895         this.update(this.activeDate.add("y", 1));
16896     },
16897
16898     
16899    // private
16900     update : function(date)
16901     {
16902         var vd = this.activeDate;
16903         this.activeDate = date;
16904 //        if(vd && this.el){
16905 //            var t = date.getTime();
16906 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16907 //                Roo.log('using add remove');
16908 //                
16909 //                this.fireEvent('monthchange', this, date);
16910 //                
16911 //                this.cells.removeClass("fc-state-highlight");
16912 //                this.cells.each(function(c){
16913 //                   if(c.dateValue == t){
16914 //                       c.addClass("fc-state-highlight");
16915 //                       setTimeout(function(){
16916 //                            try{c.dom.firstChild.focus();}catch(e){}
16917 //                       }, 50);
16918 //                       return false;
16919 //                   }
16920 //                   return true;
16921 //                });
16922 //                return;
16923 //            }
16924 //        }
16925         
16926         var days = date.getDaysInMonth();
16927         
16928         var firstOfMonth = date.getFirstDateOfMonth();
16929         var startingPos = firstOfMonth.getDay()-this.startDay;
16930         
16931         if(startingPos < this.startDay){
16932             startingPos += 7;
16933         }
16934         
16935         var pm = date.add(Date.MONTH, -1);
16936         var prevStart = pm.getDaysInMonth()-startingPos;
16937 //        
16938         this.cells = this.el.select('.fc-day',true);
16939         this.textNodes = this.el.query('.fc-day-number');
16940         this.cells.addClassOnOver('fc-state-hover');
16941         
16942         var cells = this.cells.elements;
16943         var textEls = this.textNodes;
16944         
16945         Roo.each(cells, function(cell){
16946             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16947         });
16948         
16949         days += startingPos;
16950
16951         // convert everything to numbers so it's fast
16952         var day = 86400000;
16953         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16954         //Roo.log(d);
16955         //Roo.log(pm);
16956         //Roo.log(prevStart);
16957         
16958         var today = new Date().clearTime().getTime();
16959         var sel = date.clearTime().getTime();
16960         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16961         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16962         var ddMatch = this.disabledDatesRE;
16963         var ddText = this.disabledDatesText;
16964         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16965         var ddaysText = this.disabledDaysText;
16966         var format = this.format;
16967         
16968         var setCellClass = function(cal, cell){
16969             cell.row = 0;
16970             cell.events = [];
16971             cell.more = [];
16972             //Roo.log('set Cell Class');
16973             cell.title = "";
16974             var t = d.getTime();
16975             
16976             //Roo.log(d);
16977             
16978             cell.dateValue = t;
16979             if(t == today){
16980                 cell.className += " fc-today";
16981                 cell.className += " fc-state-highlight";
16982                 cell.title = cal.todayText;
16983             }
16984             if(t == sel){
16985                 // disable highlight in other month..
16986                 //cell.className += " fc-state-highlight";
16987                 
16988             }
16989             // disabling
16990             if(t < min) {
16991                 cell.className = " fc-state-disabled";
16992                 cell.title = cal.minText;
16993                 return;
16994             }
16995             if(t > max) {
16996                 cell.className = " fc-state-disabled";
16997                 cell.title = cal.maxText;
16998                 return;
16999             }
17000             if(ddays){
17001                 if(ddays.indexOf(d.getDay()) != -1){
17002                     cell.title = ddaysText;
17003                     cell.className = " fc-state-disabled";
17004                 }
17005             }
17006             if(ddMatch && format){
17007                 var fvalue = d.dateFormat(format);
17008                 if(ddMatch.test(fvalue)){
17009                     cell.title = ddText.replace("%0", fvalue);
17010                     cell.className = " fc-state-disabled";
17011                 }
17012             }
17013             
17014             if (!cell.initialClassName) {
17015                 cell.initialClassName = cell.dom.className;
17016             }
17017             
17018             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17019         };
17020
17021         var i = 0;
17022         
17023         for(; i < startingPos; i++) {
17024             textEls[i].innerHTML = (++prevStart);
17025             d.setDate(d.getDate()+1);
17026             
17027             cells[i].className = "fc-past fc-other-month";
17028             setCellClass(this, cells[i]);
17029         }
17030         
17031         var intDay = 0;
17032         
17033         for(; i < days; i++){
17034             intDay = i - startingPos + 1;
17035             textEls[i].innerHTML = (intDay);
17036             d.setDate(d.getDate()+1);
17037             
17038             cells[i].className = ''; // "x-date-active";
17039             setCellClass(this, cells[i]);
17040         }
17041         var extraDays = 0;
17042         
17043         for(; i < 42; i++) {
17044             textEls[i].innerHTML = (++extraDays);
17045             d.setDate(d.getDate()+1);
17046             
17047             cells[i].className = "fc-future fc-other-month";
17048             setCellClass(this, cells[i]);
17049         }
17050         
17051         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17052         
17053         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17054         
17055         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17056         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17057         
17058         if(totalRows != 6){
17059             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17060             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17061         }
17062         
17063         this.fireEvent('monthchange', this, date);
17064         
17065         
17066         /*
17067         if(!this.internalRender){
17068             var main = this.el.dom.firstChild;
17069             var w = main.offsetWidth;
17070             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17071             Roo.fly(main).setWidth(w);
17072             this.internalRender = true;
17073             // opera does not respect the auto grow header center column
17074             // then, after it gets a width opera refuses to recalculate
17075             // without a second pass
17076             if(Roo.isOpera && !this.secondPass){
17077                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17078                 this.secondPass = true;
17079                 this.update.defer(10, this, [date]);
17080             }
17081         }
17082         */
17083         
17084     },
17085     
17086     findCell : function(dt) {
17087         dt = dt.clearTime().getTime();
17088         var ret = false;
17089         this.cells.each(function(c){
17090             //Roo.log("check " +c.dateValue + '?=' + dt);
17091             if(c.dateValue == dt){
17092                 ret = c;
17093                 return false;
17094             }
17095             return true;
17096         });
17097         
17098         return ret;
17099     },
17100     
17101     findCells : function(ev) {
17102         var s = ev.start.clone().clearTime().getTime();
17103        // Roo.log(s);
17104         var e= ev.end.clone().clearTime().getTime();
17105        // Roo.log(e);
17106         var ret = [];
17107         this.cells.each(function(c){
17108              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17109             
17110             if(c.dateValue > e){
17111                 return ;
17112             }
17113             if(c.dateValue < s){
17114                 return ;
17115             }
17116             ret.push(c);
17117         });
17118         
17119         return ret;    
17120     },
17121     
17122 //    findBestRow: function(cells)
17123 //    {
17124 //        var ret = 0;
17125 //        
17126 //        for (var i =0 ; i < cells.length;i++) {
17127 //            ret  = Math.max(cells[i].rows || 0,ret);
17128 //        }
17129 //        return ret;
17130 //        
17131 //    },
17132     
17133     
17134     addItem : function(ev)
17135     {
17136         // look for vertical location slot in
17137         var cells = this.findCells(ev);
17138         
17139 //        ev.row = this.findBestRow(cells);
17140         
17141         // work out the location.
17142         
17143         var crow = false;
17144         var rows = [];
17145         for(var i =0; i < cells.length; i++) {
17146             
17147             cells[i].row = cells[0].row;
17148             
17149             if(i == 0){
17150                 cells[i].row = cells[i].row + 1;
17151             }
17152             
17153             if (!crow) {
17154                 crow = {
17155                     start : cells[i],
17156                     end :  cells[i]
17157                 };
17158                 continue;
17159             }
17160             if (crow.start.getY() == cells[i].getY()) {
17161                 // on same row.
17162                 crow.end = cells[i];
17163                 continue;
17164             }
17165             // different row.
17166             rows.push(crow);
17167             crow = {
17168                 start: cells[i],
17169                 end : cells[i]
17170             };
17171             
17172         }
17173         
17174         rows.push(crow);
17175         ev.els = [];
17176         ev.rows = rows;
17177         ev.cells = cells;
17178         
17179         cells[0].events.push(ev);
17180         
17181         this.calevents.push(ev);
17182     },
17183     
17184     clearEvents: function() {
17185         
17186         if(!this.calevents){
17187             return;
17188         }
17189         
17190         Roo.each(this.cells.elements, function(c){
17191             c.row = 0;
17192             c.events = [];
17193             c.more = [];
17194         });
17195         
17196         Roo.each(this.calevents, function(e) {
17197             Roo.each(e.els, function(el) {
17198                 el.un('mouseenter' ,this.onEventEnter, this);
17199                 el.un('mouseleave' ,this.onEventLeave, this);
17200                 el.remove();
17201             },this);
17202         },this);
17203         
17204         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17205             e.remove();
17206         });
17207         
17208     },
17209     
17210     renderEvents: function()
17211     {   
17212         var _this = this;
17213         
17214         this.cells.each(function(c) {
17215             
17216             if(c.row < 5){
17217                 return;
17218             }
17219             
17220             var ev = c.events;
17221             
17222             var r = 4;
17223             if(c.row != c.events.length){
17224                 r = 4 - (4 - (c.row - c.events.length));
17225             }
17226             
17227             c.events = ev.slice(0, r);
17228             c.more = ev.slice(r);
17229             
17230             if(c.more.length && c.more.length == 1){
17231                 c.events.push(c.more.pop());
17232             }
17233             
17234             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17235             
17236         });
17237             
17238         this.cells.each(function(c) {
17239             
17240             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17241             
17242             
17243             for (var e = 0; e < c.events.length; e++){
17244                 var ev = c.events[e];
17245                 var rows = ev.rows;
17246                 
17247                 for(var i = 0; i < rows.length; i++) {
17248                 
17249                     // how many rows should it span..
17250
17251                     var  cfg = {
17252                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17253                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17254
17255                         unselectable : "on",
17256                         cn : [
17257                             {
17258                                 cls: 'fc-event-inner',
17259                                 cn : [
17260     //                                {
17261     //                                  tag:'span',
17262     //                                  cls: 'fc-event-time',
17263     //                                  html : cells.length > 1 ? '' : ev.time
17264     //                                },
17265                                     {
17266                                       tag:'span',
17267                                       cls: 'fc-event-title',
17268                                       html : String.format('{0}', ev.title)
17269                                     }
17270
17271
17272                                 ]
17273                             },
17274                             {
17275                                 cls: 'ui-resizable-handle ui-resizable-e',
17276                                 html : '&nbsp;&nbsp;&nbsp'
17277                             }
17278
17279                         ]
17280                     };
17281
17282                     if (i == 0) {
17283                         cfg.cls += ' fc-event-start';
17284                     }
17285                     if ((i+1) == rows.length) {
17286                         cfg.cls += ' fc-event-end';
17287                     }
17288
17289                     var ctr = _this.el.select('.fc-event-container',true).first();
17290                     var cg = ctr.createChild(cfg);
17291
17292                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17293                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17294
17295                     var r = (c.more.length) ? 1 : 0;
17296                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17297                     cg.setWidth(ebox.right - sbox.x -2);
17298
17299                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17300                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17301                     cg.on('click', _this.onEventClick, _this, ev);
17302
17303                     ev.els.push(cg);
17304                     
17305                 }
17306                 
17307             }
17308             
17309             
17310             if(c.more.length){
17311                 var  cfg = {
17312                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17313                     style : 'position: absolute',
17314                     unselectable : "on",
17315                     cn : [
17316                         {
17317                             cls: 'fc-event-inner',
17318                             cn : [
17319                                 {
17320                                   tag:'span',
17321                                   cls: 'fc-event-title',
17322                                   html : 'More'
17323                                 }
17324
17325
17326                             ]
17327                         },
17328                         {
17329                             cls: 'ui-resizable-handle ui-resizable-e',
17330                             html : '&nbsp;&nbsp;&nbsp'
17331                         }
17332
17333                     ]
17334                 };
17335
17336                 var ctr = _this.el.select('.fc-event-container',true).first();
17337                 var cg = ctr.createChild(cfg);
17338
17339                 var sbox = c.select('.fc-day-content',true).first().getBox();
17340                 var ebox = c.select('.fc-day-content',true).first().getBox();
17341                 //Roo.log(cg);
17342                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17343                 cg.setWidth(ebox.right - sbox.x -2);
17344
17345                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17346                 
17347             }
17348             
17349         });
17350         
17351         
17352         
17353     },
17354     
17355     onEventEnter: function (e, el,event,d) {
17356         this.fireEvent('evententer', this, el, event);
17357     },
17358     
17359     onEventLeave: function (e, el,event,d) {
17360         this.fireEvent('eventleave', this, el, event);
17361     },
17362     
17363     onEventClick: function (e, el,event,d) {
17364         this.fireEvent('eventclick', this, el, event);
17365     },
17366     
17367     onMonthChange: function () {
17368         this.store.load();
17369     },
17370     
17371     onMoreEventClick: function(e, el, more)
17372     {
17373         var _this = this;
17374         
17375         this.calpopover.placement = 'right';
17376         this.calpopover.setTitle('More');
17377         
17378         this.calpopover.setContent('');
17379         
17380         var ctr = this.calpopover.el.select('.popover-content', true).first();
17381         
17382         Roo.each(more, function(m){
17383             var cfg = {
17384                 cls : 'fc-event-hori fc-event-draggable',
17385                 html : m.title
17386             };
17387             var cg = ctr.createChild(cfg);
17388             
17389             cg.on('click', _this.onEventClick, _this, m);
17390         });
17391         
17392         this.calpopover.show(el);
17393         
17394         
17395     },
17396     
17397     onLoad: function () 
17398     {   
17399         this.calevents = [];
17400         var cal = this;
17401         
17402         if(this.store.getCount() > 0){
17403             this.store.data.each(function(d){
17404                cal.addItem({
17405                     id : d.data.id,
17406                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17407                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17408                     time : d.data.start_time,
17409                     title : d.data.title,
17410                     description : d.data.description,
17411                     venue : d.data.venue
17412                 });
17413             });
17414         }
17415         
17416         this.renderEvents();
17417         
17418         if(this.calevents.length && this.loadMask){
17419             this.maskEl.hide();
17420         }
17421     },
17422     
17423     onBeforeLoad: function()
17424     {
17425         this.clearEvents();
17426         if(this.loadMask){
17427             this.maskEl.show();
17428         }
17429     }
17430 });
17431
17432  
17433  /*
17434  * - LGPL
17435  *
17436  * element
17437  * 
17438  */
17439
17440 /**
17441  * @class Roo.bootstrap.Popover
17442  * @extends Roo.bootstrap.Component
17443  * Bootstrap Popover class
17444  * @cfg {String} html contents of the popover   (or false to use children..)
17445  * @cfg {String} title of popover (or false to hide)
17446  * @cfg {String} placement how it is placed
17447  * @cfg {String} trigger click || hover (or false to trigger manually)
17448  * @cfg {String} over what (parent or false to trigger manually.)
17449  * @cfg {Number} delay - delay before showing
17450  
17451  * @constructor
17452  * Create a new Popover
17453  * @param {Object} config The config object
17454  */
17455
17456 Roo.bootstrap.Popover = function(config){
17457     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17458     
17459     this.addEvents({
17460         // raw events
17461          /**
17462          * @event show
17463          * After the popover show
17464          * 
17465          * @param {Roo.bootstrap.Popover} this
17466          */
17467         "show" : true,
17468         /**
17469          * @event hide
17470          * After the popover hide
17471          * 
17472          * @param {Roo.bootstrap.Popover} this
17473          */
17474         "hide" : true
17475     });
17476 };
17477
17478 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17479     
17480     title: 'Fill in a title',
17481     html: false,
17482     
17483     placement : 'right',
17484     trigger : 'hover', // hover
17485     
17486     delay : 0,
17487     
17488     over: 'parent',
17489     
17490     can_build_overlaid : false,
17491     
17492     getChildContainer : function()
17493     {
17494         return this.el.select('.popover-content',true).first();
17495     },
17496     
17497     getAutoCreate : function(){
17498          
17499         var cfg = {
17500            cls : 'popover roo-dynamic',
17501            style: 'display:block',
17502            cn : [
17503                 {
17504                     cls : 'arrow'
17505                 },
17506                 {
17507                     cls : 'popover-inner',
17508                     cn : [
17509                         {
17510                             tag: 'h3',
17511                             cls: 'popover-title',
17512                             html : this.title
17513                         },
17514                         {
17515                             cls : 'popover-content',
17516                             html : this.html
17517                         }
17518                     ]
17519                     
17520                 }
17521            ]
17522         };
17523         
17524         return cfg;
17525     },
17526     setTitle: function(str)
17527     {
17528         this.title = str;
17529         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17530     },
17531     setContent: function(str)
17532     {
17533         this.html = str;
17534         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17535     },
17536     // as it get's added to the bottom of the page.
17537     onRender : function(ct, position)
17538     {
17539         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17540         if(!this.el){
17541             var cfg = Roo.apply({},  this.getAutoCreate());
17542             cfg.id = Roo.id();
17543             
17544             if (this.cls) {
17545                 cfg.cls += ' ' + this.cls;
17546             }
17547             if (this.style) {
17548                 cfg.style = this.style;
17549             }
17550             //Roo.log("adding to ");
17551             this.el = Roo.get(document.body).createChild(cfg, position);
17552 //            Roo.log(this.el);
17553         }
17554         this.initEvents();
17555     },
17556     
17557     initEvents : function()
17558     {
17559         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17560         this.el.enableDisplayMode('block');
17561         this.el.hide();
17562         if (this.over === false) {
17563             return; 
17564         }
17565         if (this.triggers === false) {
17566             return;
17567         }
17568         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17569         var triggers = this.trigger ? this.trigger.split(' ') : [];
17570         Roo.each(triggers, function(trigger) {
17571         
17572             if (trigger == 'click') {
17573                 on_el.on('click', this.toggle, this);
17574             } else if (trigger != 'manual') {
17575                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17576                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17577       
17578                 on_el.on(eventIn  ,this.enter, this);
17579                 on_el.on(eventOut, this.leave, this);
17580             }
17581         }, this);
17582         
17583     },
17584     
17585     
17586     // private
17587     timeout : null,
17588     hoverState : null,
17589     
17590     toggle : function () {
17591         this.hoverState == 'in' ? this.leave() : this.enter();
17592     },
17593     
17594     enter : function () {
17595         
17596         clearTimeout(this.timeout);
17597     
17598         this.hoverState = 'in';
17599     
17600         if (!this.delay || !this.delay.show) {
17601             this.show();
17602             return;
17603         }
17604         var _t = this;
17605         this.timeout = setTimeout(function () {
17606             if (_t.hoverState == 'in') {
17607                 _t.show();
17608             }
17609         }, this.delay.show)
17610     },
17611     
17612     leave : function() {
17613         clearTimeout(this.timeout);
17614     
17615         this.hoverState = 'out';
17616     
17617         if (!this.delay || !this.delay.hide) {
17618             this.hide();
17619             return;
17620         }
17621         var _t = this;
17622         this.timeout = setTimeout(function () {
17623             if (_t.hoverState == 'out') {
17624                 _t.hide();
17625             }
17626         }, this.delay.hide)
17627     },
17628     
17629     show : function (on_el)
17630     {
17631         if (!on_el) {
17632             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17633         }
17634         
17635         // set content.
17636         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17637         if (this.html !== false) {
17638             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17639         }
17640         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17641         if (!this.title.length) {
17642             this.el.select('.popover-title',true).hide();
17643         }
17644         
17645         var placement = typeof this.placement == 'function' ?
17646             this.placement.call(this, this.el, on_el) :
17647             this.placement;
17648             
17649         var autoToken = /\s?auto?\s?/i;
17650         var autoPlace = autoToken.test(placement);
17651         if (autoPlace) {
17652             placement = placement.replace(autoToken, '') || 'top';
17653         }
17654         
17655         //this.el.detach()
17656         //this.el.setXY([0,0]);
17657         this.el.show();
17658         this.el.dom.style.display='block';
17659         this.el.addClass(placement);
17660         
17661         //this.el.appendTo(on_el);
17662         
17663         var p = this.getPosition();
17664         var box = this.el.getBox();
17665         
17666         if (autoPlace) {
17667             // fixme..
17668         }
17669         var align = Roo.bootstrap.Popover.alignment[placement];
17670         
17671 //        Roo.log(align);
17672         this.el.alignTo(on_el, align[0],align[1]);
17673         //var arrow = this.el.select('.arrow',true).first();
17674         //arrow.set(align[2], 
17675         
17676         this.el.addClass('in');
17677         
17678         
17679         if (this.el.hasClass('fade')) {
17680             // fade it?
17681         }
17682         
17683         this.hoverState = 'in';
17684         
17685         this.fireEvent('show', this);
17686         
17687     },
17688     hide : function()
17689     {
17690         this.el.setXY([0,0]);
17691         this.el.removeClass('in');
17692         this.el.hide();
17693         this.hoverState = null;
17694         
17695         this.fireEvent('hide', this);
17696     }
17697     
17698 });
17699
17700 Roo.bootstrap.Popover.alignment = {
17701     'left' : ['r-l', [-10,0], 'right'],
17702     'right' : ['l-r', [10,0], 'left'],
17703     'bottom' : ['t-b', [0,10], 'top'],
17704     'top' : [ 'b-t', [0,-10], 'bottom']
17705 };
17706
17707  /*
17708  * - LGPL
17709  *
17710  * Progress
17711  * 
17712  */
17713
17714 /**
17715  * @class Roo.bootstrap.Progress
17716  * @extends Roo.bootstrap.Component
17717  * Bootstrap Progress class
17718  * @cfg {Boolean} striped striped of the progress bar
17719  * @cfg {Boolean} active animated of the progress bar
17720  * 
17721  * 
17722  * @constructor
17723  * Create a new Progress
17724  * @param {Object} config The config object
17725  */
17726
17727 Roo.bootstrap.Progress = function(config){
17728     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17729 };
17730
17731 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17732     
17733     striped : false,
17734     active: false,
17735     
17736     getAutoCreate : function(){
17737         var cfg = {
17738             tag: 'div',
17739             cls: 'progress'
17740         };
17741         
17742         
17743         if(this.striped){
17744             cfg.cls += ' progress-striped';
17745         }
17746       
17747         if(this.active){
17748             cfg.cls += ' active';
17749         }
17750         
17751         
17752         return cfg;
17753     }
17754    
17755 });
17756
17757  
17758
17759  /*
17760  * - LGPL
17761  *
17762  * ProgressBar
17763  * 
17764  */
17765
17766 /**
17767  * @class Roo.bootstrap.ProgressBar
17768  * @extends Roo.bootstrap.Component
17769  * Bootstrap ProgressBar class
17770  * @cfg {Number} aria_valuenow aria-value now
17771  * @cfg {Number} aria_valuemin aria-value min
17772  * @cfg {Number} aria_valuemax aria-value max
17773  * @cfg {String} label label for the progress bar
17774  * @cfg {String} panel (success | info | warning | danger )
17775  * @cfg {String} role role of the progress bar
17776  * @cfg {String} sr_only text
17777  * 
17778  * 
17779  * @constructor
17780  * Create a new ProgressBar
17781  * @param {Object} config The config object
17782  */
17783
17784 Roo.bootstrap.ProgressBar = function(config){
17785     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17786 };
17787
17788 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17789     
17790     aria_valuenow : 0,
17791     aria_valuemin : 0,
17792     aria_valuemax : 100,
17793     label : false,
17794     panel : false,
17795     role : false,
17796     sr_only: false,
17797     
17798     getAutoCreate : function()
17799     {
17800         
17801         var cfg = {
17802             tag: 'div',
17803             cls: 'progress-bar',
17804             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17805         };
17806         
17807         if(this.sr_only){
17808             cfg.cn = {
17809                 tag: 'span',
17810                 cls: 'sr-only',
17811                 html: this.sr_only
17812             }
17813         }
17814         
17815         if(this.role){
17816             cfg.role = this.role;
17817         }
17818         
17819         if(this.aria_valuenow){
17820             cfg['aria-valuenow'] = this.aria_valuenow;
17821         }
17822         
17823         if(this.aria_valuemin){
17824             cfg['aria-valuemin'] = this.aria_valuemin;
17825         }
17826         
17827         if(this.aria_valuemax){
17828             cfg['aria-valuemax'] = this.aria_valuemax;
17829         }
17830         
17831         if(this.label && !this.sr_only){
17832             cfg.html = this.label;
17833         }
17834         
17835         if(this.panel){
17836             cfg.cls += ' progress-bar-' + this.panel;
17837         }
17838         
17839         return cfg;
17840     },
17841     
17842     update : function(aria_valuenow)
17843     {
17844         this.aria_valuenow = aria_valuenow;
17845         
17846         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17847     }
17848    
17849 });
17850
17851  
17852
17853  /*
17854  * - LGPL
17855  *
17856  * column
17857  * 
17858  */
17859
17860 /**
17861  * @class Roo.bootstrap.TabGroup
17862  * @extends Roo.bootstrap.Column
17863  * Bootstrap Column class
17864  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17865  * @cfg {Boolean} carousel true to make the group behave like a carousel
17866  * @cfg {Boolean} bullets show bullets for the panels
17867  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17868  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17869  * @cfg {Boolean} showarrow (true|false) show arrow default true
17870  * 
17871  * @constructor
17872  * Create a new TabGroup
17873  * @param {Object} config The config object
17874  */
17875
17876 Roo.bootstrap.TabGroup = function(config){
17877     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17878     if (!this.navId) {
17879         this.navId = Roo.id();
17880     }
17881     this.tabs = [];
17882     Roo.bootstrap.TabGroup.register(this);
17883     
17884 };
17885
17886 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17887     
17888     carousel : false,
17889     transition : false,
17890     bullets : 0,
17891     timer : 0,
17892     autoslide : false,
17893     slideFn : false,
17894     slideOnTouch : false,
17895     showarrow : true,
17896     
17897     getAutoCreate : function()
17898     {
17899         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17900         
17901         cfg.cls += ' tab-content';
17902         
17903         if (this.carousel) {
17904             cfg.cls += ' carousel slide';
17905             
17906             cfg.cn = [{
17907                cls : 'carousel-inner',
17908                cn : []
17909             }];
17910         
17911             if(this.bullets  && !Roo.isTouch){
17912                 
17913                 var bullets = {
17914                     cls : 'carousel-bullets',
17915                     cn : []
17916                 };
17917                
17918                 if(this.bullets_cls){
17919                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17920                 }
17921                 
17922                 bullets.cn.push({
17923                     cls : 'clear'
17924                 });
17925                 
17926                 cfg.cn[0].cn.push(bullets);
17927             }
17928             
17929             if(this.showarrow){
17930                 cfg.cn[0].cn.push({
17931                     tag : 'div',
17932                     class : 'carousel-arrow',
17933                     cn : [
17934                         {
17935                             tag : 'div',
17936                             class : 'carousel-prev',
17937                             cn : [
17938                                 {
17939                                     tag : 'i',
17940                                     class : 'fa fa-chevron-left'
17941                                 }
17942                             ]
17943                         },
17944                         {
17945                             tag : 'div',
17946                             class : 'carousel-next',
17947                             cn : [
17948                                 {
17949                                     tag : 'i',
17950                                     class : 'fa fa-chevron-right'
17951                                 }
17952                             ]
17953                         }
17954                     ]
17955                 });
17956             }
17957             
17958         }
17959         
17960         return cfg;
17961     },
17962     
17963     initEvents:  function()
17964     {
17965 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17966 //            this.el.on("touchstart", this.onTouchStart, this);
17967 //        }
17968         
17969         if(this.autoslide){
17970             var _this = this;
17971             
17972             this.slideFn = window.setInterval(function() {
17973                 _this.showPanelNext();
17974             }, this.timer);
17975         }
17976         
17977         if(this.showarrow){
17978             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17979             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17980         }
17981         
17982         
17983     },
17984     
17985 //    onTouchStart : function(e, el, o)
17986 //    {
17987 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17988 //            return;
17989 //        }
17990 //        
17991 //        this.showPanelNext();
17992 //    },
17993     
17994     
17995     getChildContainer : function()
17996     {
17997         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17998     },
17999     
18000     /**
18001     * register a Navigation item
18002     * @param {Roo.bootstrap.NavItem} the navitem to add
18003     */
18004     register : function(item)
18005     {
18006         this.tabs.push( item);
18007         item.navId = this.navId; // not really needed..
18008         this.addBullet();
18009     
18010     },
18011     
18012     getActivePanel : function()
18013     {
18014         var r = false;
18015         Roo.each(this.tabs, function(t) {
18016             if (t.active) {
18017                 r = t;
18018                 return false;
18019             }
18020             return null;
18021         });
18022         return r;
18023         
18024     },
18025     getPanelByName : function(n)
18026     {
18027         var r = false;
18028         Roo.each(this.tabs, function(t) {
18029             if (t.tabId == n) {
18030                 r = t;
18031                 return false;
18032             }
18033             return null;
18034         });
18035         return r;
18036     },
18037     indexOfPanel : function(p)
18038     {
18039         var r = false;
18040         Roo.each(this.tabs, function(t,i) {
18041             if (t.tabId == p.tabId) {
18042                 r = i;
18043                 return false;
18044             }
18045             return null;
18046         });
18047         return r;
18048     },
18049     /**
18050      * show a specific panel
18051      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18052      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18053      */
18054     showPanel : function (pan)
18055     {
18056         if(this.transition || typeof(pan) == 'undefined'){
18057             Roo.log("waiting for the transitionend");
18058             return;
18059         }
18060         
18061         if (typeof(pan) == 'number') {
18062             pan = this.tabs[pan];
18063         }
18064         
18065         if (typeof(pan) == 'string') {
18066             pan = this.getPanelByName(pan);
18067         }
18068         
18069         var cur = this.getActivePanel();
18070         
18071         if(!pan || !cur){
18072             Roo.log('pan or acitve pan is undefined');
18073             return false;
18074         }
18075         
18076         if (pan.tabId == this.getActivePanel().tabId) {
18077             return true;
18078         }
18079         
18080         if (false === cur.fireEvent('beforedeactivate')) {
18081             return false;
18082         }
18083         
18084         if(this.bullets > 0 && !Roo.isTouch){
18085             this.setActiveBullet(this.indexOfPanel(pan));
18086         }
18087         
18088         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18089             
18090             this.transition = true;
18091             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18092             var lr = dir == 'next' ? 'left' : 'right';
18093             pan.el.addClass(dir); // or prev
18094             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18095             cur.el.addClass(lr); // or right
18096             pan.el.addClass(lr);
18097             
18098             var _this = this;
18099             cur.el.on('transitionend', function() {
18100                 Roo.log("trans end?");
18101                 
18102                 pan.el.removeClass([lr,dir]);
18103                 pan.setActive(true);
18104                 
18105                 cur.el.removeClass([lr]);
18106                 cur.setActive(false);
18107                 
18108                 _this.transition = false;
18109                 
18110             }, this, { single:  true } );
18111             
18112             return true;
18113         }
18114         
18115         cur.setActive(false);
18116         pan.setActive(true);
18117         
18118         return true;
18119         
18120     },
18121     showPanelNext : function()
18122     {
18123         var i = this.indexOfPanel(this.getActivePanel());
18124         
18125         if (i >= this.tabs.length - 1 && !this.autoslide) {
18126             return;
18127         }
18128         
18129         if (i >= this.tabs.length - 1 && this.autoslide) {
18130             i = -1;
18131         }
18132         
18133         this.showPanel(this.tabs[i+1]);
18134     },
18135     
18136     showPanelPrev : function()
18137     {
18138         var i = this.indexOfPanel(this.getActivePanel());
18139         
18140         if (i  < 1 && !this.autoslide) {
18141             return;
18142         }
18143         
18144         if (i < 1 && this.autoslide) {
18145             i = this.tabs.length;
18146         }
18147         
18148         this.showPanel(this.tabs[i-1]);
18149     },
18150     
18151     
18152     addBullet: function()
18153     {
18154         if(!this.bullets || Roo.isTouch){
18155             return;
18156         }
18157         var ctr = this.el.select('.carousel-bullets',true).first();
18158         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18159         var bullet = ctr.createChild({
18160             cls : 'bullet bullet-' + i
18161         },ctr.dom.lastChild);
18162         
18163         
18164         var _this = this;
18165         
18166         bullet.on('click', (function(e, el, o, ii, t){
18167
18168             e.preventDefault();
18169
18170             this.showPanel(ii);
18171
18172             if(this.autoslide && this.slideFn){
18173                 clearInterval(this.slideFn);
18174                 this.slideFn = window.setInterval(function() {
18175                     _this.showPanelNext();
18176                 }, this.timer);
18177             }
18178
18179         }).createDelegate(this, [i, bullet], true));
18180                 
18181         
18182     },
18183      
18184     setActiveBullet : function(i)
18185     {
18186         if(Roo.isTouch){
18187             return;
18188         }
18189         
18190         Roo.each(this.el.select('.bullet', true).elements, function(el){
18191             el.removeClass('selected');
18192         });
18193
18194         var bullet = this.el.select('.bullet-' + i, true).first();
18195         
18196         if(!bullet){
18197             return;
18198         }
18199         
18200         bullet.addClass('selected');
18201     }
18202     
18203     
18204   
18205 });
18206
18207  
18208
18209  
18210  
18211 Roo.apply(Roo.bootstrap.TabGroup, {
18212     
18213     groups: {},
18214      /**
18215     * register a Navigation Group
18216     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18217     */
18218     register : function(navgrp)
18219     {
18220         this.groups[navgrp.navId] = navgrp;
18221         
18222     },
18223     /**
18224     * fetch a Navigation Group based on the navigation ID
18225     * if one does not exist , it will get created.
18226     * @param {string} the navgroup to add
18227     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18228     */
18229     get: function(navId) {
18230         if (typeof(this.groups[navId]) == 'undefined') {
18231             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18232         }
18233         return this.groups[navId] ;
18234     }
18235     
18236     
18237     
18238 });
18239
18240  /*
18241  * - LGPL
18242  *
18243  * TabPanel
18244  * 
18245  */
18246
18247 /**
18248  * @class Roo.bootstrap.TabPanel
18249  * @extends Roo.bootstrap.Component
18250  * Bootstrap TabPanel class
18251  * @cfg {Boolean} active panel active
18252  * @cfg {String} html panel content
18253  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18254  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18255  * @cfg {String} href click to link..
18256  * 
18257  * 
18258  * @constructor
18259  * Create a new TabPanel
18260  * @param {Object} config The config object
18261  */
18262
18263 Roo.bootstrap.TabPanel = function(config){
18264     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18265     this.addEvents({
18266         /**
18267              * @event changed
18268              * Fires when the active status changes
18269              * @param {Roo.bootstrap.TabPanel} this
18270              * @param {Boolean} state the new state
18271             
18272          */
18273         'changed': true,
18274         /**
18275              * @event beforedeactivate
18276              * Fires before a tab is de-activated - can be used to do validation on a form.
18277              * @param {Roo.bootstrap.TabPanel} this
18278              * @return {Boolean} false if there is an error
18279             
18280          */
18281         'beforedeactivate': true
18282      });
18283     
18284     this.tabId = this.tabId || Roo.id();
18285   
18286 };
18287
18288 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18289     
18290     active: false,
18291     html: false,
18292     tabId: false,
18293     navId : false,
18294     href : '',
18295     
18296     getAutoCreate : function(){
18297         var cfg = {
18298             tag: 'div',
18299             // item is needed for carousel - not sure if it has any effect otherwise
18300             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18301             html: this.html || ''
18302         };
18303         
18304         if(this.active){
18305             cfg.cls += ' active';
18306         }
18307         
18308         if(this.tabId){
18309             cfg.tabId = this.tabId;
18310         }
18311         
18312         
18313         return cfg;
18314     },
18315     
18316     initEvents:  function()
18317     {
18318         var p = this.parent();
18319         
18320         this.navId = this.navId || p.navId;
18321         
18322         if (typeof(this.navId) != 'undefined') {
18323             // not really needed.. but just in case.. parent should be a NavGroup.
18324             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18325             
18326             tg.register(this);
18327             
18328             var i = tg.tabs.length - 1;
18329             
18330             if(this.active && tg.bullets > 0 && i < tg.bullets){
18331                 tg.setActiveBullet(i);
18332             }
18333         }
18334         
18335         this.el.on('click', this.onClick, this);
18336         
18337         if(Roo.isTouch){
18338             this.el.on("touchstart", this.onTouchStart, this);
18339             this.el.on("touchmove", this.onTouchMove, this);
18340             this.el.on("touchend", this.onTouchEnd, this);
18341         }
18342         
18343     },
18344     
18345     onRender : function(ct, position)
18346     {
18347         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18348     },
18349     
18350     setActive : function(state)
18351     {
18352         Roo.log("panel - set active " + this.tabId + "=" + state);
18353         
18354         this.active = state;
18355         if (!state) {
18356             this.el.removeClass('active');
18357             
18358         } else  if (!this.el.hasClass('active')) {
18359             this.el.addClass('active');
18360         }
18361         
18362         this.fireEvent('changed', this, state);
18363     },
18364     
18365     onClick : function(e)
18366     {
18367         e.preventDefault();
18368         
18369         if(!this.href.length){
18370             return;
18371         }
18372         
18373         window.location.href = this.href;
18374     },
18375     
18376     startX : 0,
18377     startY : 0,
18378     endX : 0,
18379     endY : 0,
18380     swiping : false,
18381     
18382     onTouchStart : function(e)
18383     {
18384         this.swiping = false;
18385         
18386         this.startX = e.browserEvent.touches[0].clientX;
18387         this.startY = e.browserEvent.touches[0].clientY;
18388     },
18389     
18390     onTouchMove : function(e)
18391     {
18392         this.swiping = true;
18393         
18394         this.endX = e.browserEvent.touches[0].clientX;
18395         this.endY = e.browserEvent.touches[0].clientY;
18396     },
18397     
18398     onTouchEnd : function(e)
18399     {
18400         if(!this.swiping){
18401             this.onClick(e);
18402             return;
18403         }
18404         
18405         var tabGroup = this.parent();
18406         
18407         if(this.endX > this.startX){ // swiping right
18408             tabGroup.showPanelPrev();
18409             return;
18410         }
18411         
18412         if(this.startX > this.endX){ // swiping left
18413             tabGroup.showPanelNext();
18414             return;
18415         }
18416     }
18417     
18418     
18419 });
18420  
18421
18422  
18423
18424  /*
18425  * - LGPL
18426  *
18427  * DateField
18428  * 
18429  */
18430
18431 /**
18432  * @class Roo.bootstrap.DateField
18433  * @extends Roo.bootstrap.Input
18434  * Bootstrap DateField class
18435  * @cfg {Number} weekStart default 0
18436  * @cfg {String} viewMode default empty, (months|years)
18437  * @cfg {String} minViewMode default empty, (months|years)
18438  * @cfg {Number} startDate default -Infinity
18439  * @cfg {Number} endDate default Infinity
18440  * @cfg {Boolean} todayHighlight default false
18441  * @cfg {Boolean} todayBtn default false
18442  * @cfg {Boolean} calendarWeeks default false
18443  * @cfg {Object} daysOfWeekDisabled default empty
18444  * @cfg {Boolean} singleMode default false (true | false)
18445  * 
18446  * @cfg {Boolean} keyboardNavigation default true
18447  * @cfg {String} language default en
18448  * 
18449  * @constructor
18450  * Create a new DateField
18451  * @param {Object} config The config object
18452  */
18453
18454 Roo.bootstrap.DateField = function(config){
18455     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18456      this.addEvents({
18457             /**
18458              * @event show
18459              * Fires when this field show.
18460              * @param {Roo.bootstrap.DateField} this
18461              * @param {Mixed} date The date value
18462              */
18463             show : true,
18464             /**
18465              * @event show
18466              * Fires when this field hide.
18467              * @param {Roo.bootstrap.DateField} this
18468              * @param {Mixed} date The date value
18469              */
18470             hide : true,
18471             /**
18472              * @event select
18473              * Fires when select a date.
18474              * @param {Roo.bootstrap.DateField} this
18475              * @param {Mixed} date The date value
18476              */
18477             select : true,
18478             /**
18479              * @event beforeselect
18480              * Fires when before select a date.
18481              * @param {Roo.bootstrap.DateField} this
18482              * @param {Mixed} date The date value
18483              */
18484             beforeselect : true
18485         });
18486 };
18487
18488 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18489     
18490     /**
18491      * @cfg {String} format
18492      * The default date format string which can be overriden for localization support.  The format must be
18493      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18494      */
18495     format : "m/d/y",
18496     /**
18497      * @cfg {String} altFormats
18498      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18499      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18500      */
18501     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18502     
18503     weekStart : 0,
18504     
18505     viewMode : '',
18506     
18507     minViewMode : '',
18508     
18509     todayHighlight : false,
18510     
18511     todayBtn: false,
18512     
18513     language: 'en',
18514     
18515     keyboardNavigation: true,
18516     
18517     calendarWeeks: false,
18518     
18519     startDate: -Infinity,
18520     
18521     endDate: Infinity,
18522     
18523     daysOfWeekDisabled: [],
18524     
18525     _events: [],
18526     
18527     singleMode : false,
18528     
18529     UTCDate: function()
18530     {
18531         return new Date(Date.UTC.apply(Date, arguments));
18532     },
18533     
18534     UTCToday: function()
18535     {
18536         var today = new Date();
18537         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18538     },
18539     
18540     getDate: function() {
18541             var d = this.getUTCDate();
18542             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18543     },
18544     
18545     getUTCDate: function() {
18546             return this.date;
18547     },
18548     
18549     setDate: function(d) {
18550             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18551     },
18552     
18553     setUTCDate: function(d) {
18554             this.date = d;
18555             this.setValue(this.formatDate(this.date));
18556     },
18557         
18558     onRender: function(ct, position)
18559     {
18560         
18561         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18562         
18563         this.language = this.language || 'en';
18564         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18565         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18566         
18567         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18568         this.format = this.format || 'm/d/y';
18569         this.isInline = false;
18570         this.isInput = true;
18571         this.component = this.el.select('.add-on', true).first() || false;
18572         this.component = (this.component && this.component.length === 0) ? false : this.component;
18573         this.hasInput = this.component && this.inputEl().length;
18574         
18575         if (typeof(this.minViewMode === 'string')) {
18576             switch (this.minViewMode) {
18577                 case 'months':
18578                     this.minViewMode = 1;
18579                     break;
18580                 case 'years':
18581                     this.minViewMode = 2;
18582                     break;
18583                 default:
18584                     this.minViewMode = 0;
18585                     break;
18586             }
18587         }
18588         
18589         if (typeof(this.viewMode === 'string')) {
18590             switch (this.viewMode) {
18591                 case 'months':
18592                     this.viewMode = 1;
18593                     break;
18594                 case 'years':
18595                     this.viewMode = 2;
18596                     break;
18597                 default:
18598                     this.viewMode = 0;
18599                     break;
18600             }
18601         }
18602                 
18603         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18604         
18605 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18606         
18607         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18608         
18609         this.picker().on('mousedown', this.onMousedown, this);
18610         this.picker().on('click', this.onClick, this);
18611         
18612         this.picker().addClass('datepicker-dropdown');
18613         
18614         this.startViewMode = this.viewMode;
18615         
18616         if(this.singleMode){
18617             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18618                 v.setVisibilityMode(Roo.Element.DISPLAY);
18619                 v.hide();
18620             });
18621             
18622             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18623                 v.setStyle('width', '189px');
18624             });
18625         }
18626         
18627         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18628             if(!this.calendarWeeks){
18629                 v.remove();
18630                 return;
18631             }
18632             
18633             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18634             v.attr('colspan', function(i, val){
18635                 return parseInt(val) + 1;
18636             });
18637         });
18638                         
18639         
18640         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18641         
18642         this.setStartDate(this.startDate);
18643         this.setEndDate(this.endDate);
18644         
18645         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18646         
18647         this.fillDow();
18648         this.fillMonths();
18649         this.update();
18650         this.showMode();
18651         
18652         if(this.isInline) {
18653             this.showPopup();
18654         }
18655     },
18656     
18657     picker : function()
18658     {
18659         return this.pickerEl;
18660 //        return this.el.select('.datepicker', true).first();
18661     },
18662     
18663     fillDow: function()
18664     {
18665         var dowCnt = this.weekStart;
18666         
18667         var dow = {
18668             tag: 'tr',
18669             cn: [
18670                 
18671             ]
18672         };
18673         
18674         if(this.calendarWeeks){
18675             dow.cn.push({
18676                 tag: 'th',
18677                 cls: 'cw',
18678                 html: '&nbsp;'
18679             })
18680         }
18681         
18682         while (dowCnt < this.weekStart + 7) {
18683             dow.cn.push({
18684                 tag: 'th',
18685                 cls: 'dow',
18686                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18687             });
18688         }
18689         
18690         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18691     },
18692     
18693     fillMonths: function()
18694     {    
18695         var i = 0;
18696         var months = this.picker().select('>.datepicker-months td', true).first();
18697         
18698         months.dom.innerHTML = '';
18699         
18700         while (i < 12) {
18701             var month = {
18702                 tag: 'span',
18703                 cls: 'month',
18704                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18705             };
18706             
18707             months.createChild(month);
18708         }
18709         
18710     },
18711     
18712     update: function()
18713     {
18714         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;
18715         
18716         if (this.date < this.startDate) {
18717             this.viewDate = new Date(this.startDate);
18718         } else if (this.date > this.endDate) {
18719             this.viewDate = new Date(this.endDate);
18720         } else {
18721             this.viewDate = new Date(this.date);
18722         }
18723         
18724         this.fill();
18725     },
18726     
18727     fill: function() 
18728     {
18729         var d = new Date(this.viewDate),
18730                 year = d.getUTCFullYear(),
18731                 month = d.getUTCMonth(),
18732                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18733                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18734                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18735                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18736                 currentDate = this.date && this.date.valueOf(),
18737                 today = this.UTCToday();
18738         
18739         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18740         
18741 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18742         
18743 //        this.picker.select('>tfoot th.today').
18744 //                                              .text(dates[this.language].today)
18745 //                                              .toggle(this.todayBtn !== false);
18746     
18747         this.updateNavArrows();
18748         this.fillMonths();
18749                                                 
18750         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18751         
18752         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18753          
18754         prevMonth.setUTCDate(day);
18755         
18756         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18757         
18758         var nextMonth = new Date(prevMonth);
18759         
18760         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18761         
18762         nextMonth = nextMonth.valueOf();
18763         
18764         var fillMonths = false;
18765         
18766         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18767         
18768         while(prevMonth.valueOf() <= nextMonth) {
18769             var clsName = '';
18770             
18771             if (prevMonth.getUTCDay() === this.weekStart) {
18772                 if(fillMonths){
18773                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18774                 }
18775                     
18776                 fillMonths = {
18777                     tag: 'tr',
18778                     cn: []
18779                 };
18780                 
18781                 if(this.calendarWeeks){
18782                     // ISO 8601: First week contains first thursday.
18783                     // ISO also states week starts on Monday, but we can be more abstract here.
18784                     var
18785                     // Start of current week: based on weekstart/current date
18786                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18787                     // Thursday of this week
18788                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18789                     // First Thursday of year, year from thursday
18790                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18791                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18792                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18793                     
18794                     fillMonths.cn.push({
18795                         tag: 'td',
18796                         cls: 'cw',
18797                         html: calWeek
18798                     });
18799                 }
18800             }
18801             
18802             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18803                 clsName += ' old';
18804             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18805                 clsName += ' new';
18806             }
18807             if (this.todayHighlight &&
18808                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18809                 prevMonth.getUTCMonth() == today.getMonth() &&
18810                 prevMonth.getUTCDate() == today.getDate()) {
18811                 clsName += ' today';
18812             }
18813             
18814             if (currentDate && prevMonth.valueOf() === currentDate) {
18815                 clsName += ' active';
18816             }
18817             
18818             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18819                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18820                     clsName += ' disabled';
18821             }
18822             
18823             fillMonths.cn.push({
18824                 tag: 'td',
18825                 cls: 'day ' + clsName,
18826                 html: prevMonth.getDate()
18827             });
18828             
18829             prevMonth.setDate(prevMonth.getDate()+1);
18830         }
18831           
18832         var currentYear = this.date && this.date.getUTCFullYear();
18833         var currentMonth = this.date && this.date.getUTCMonth();
18834         
18835         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18836         
18837         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18838             v.removeClass('active');
18839             
18840             if(currentYear === year && k === currentMonth){
18841                 v.addClass('active');
18842             }
18843             
18844             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18845                 v.addClass('disabled');
18846             }
18847             
18848         });
18849         
18850         
18851         year = parseInt(year/10, 10) * 10;
18852         
18853         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18854         
18855         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18856         
18857         year -= 1;
18858         for (var i = -1; i < 11; i++) {
18859             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18860                 tag: 'span',
18861                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18862                 html: year
18863             });
18864             
18865             year += 1;
18866         }
18867     },
18868     
18869     showMode: function(dir) 
18870     {
18871         if (dir) {
18872             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18873         }
18874         
18875         Roo.each(this.picker().select('>div',true).elements, function(v){
18876             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18877             v.hide();
18878         });
18879         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18880     },
18881     
18882     place: function()
18883     {
18884         if(this.isInline) {
18885             return;
18886         }
18887         
18888         this.picker().removeClass(['bottom', 'top']);
18889         
18890         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18891             /*
18892              * place to the top of element!
18893              *
18894              */
18895             
18896             this.picker().addClass('top');
18897             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18898             
18899             return;
18900         }
18901         
18902         this.picker().addClass('bottom');
18903         
18904         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18905     },
18906     
18907     parseDate : function(value)
18908     {
18909         if(!value || value instanceof Date){
18910             return value;
18911         }
18912         var v = Date.parseDate(value, this.format);
18913         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18914             v = Date.parseDate(value, 'Y-m-d');
18915         }
18916         if(!v && this.altFormats){
18917             if(!this.altFormatsArray){
18918                 this.altFormatsArray = this.altFormats.split("|");
18919             }
18920             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18921                 v = Date.parseDate(value, this.altFormatsArray[i]);
18922             }
18923         }
18924         return v;
18925     },
18926     
18927     formatDate : function(date, fmt)
18928     {   
18929         return (!date || !(date instanceof Date)) ?
18930         date : date.dateFormat(fmt || this.format);
18931     },
18932     
18933     onFocus : function()
18934     {
18935         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18936         this.showPopup();
18937     },
18938     
18939     onBlur : function()
18940     {
18941         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18942         
18943         var d = this.inputEl().getValue();
18944         
18945         this.setValue(d);
18946                 
18947         this.hidePopup();
18948     },
18949     
18950     showPopup : function()
18951     {
18952         this.picker().show();
18953         this.update();
18954         this.place();
18955         
18956         this.fireEvent('showpopup', this, this.date);
18957     },
18958     
18959     hidePopup : function()
18960     {
18961         if(this.isInline) {
18962             return;
18963         }
18964         this.picker().hide();
18965         this.viewMode = this.startViewMode;
18966         this.showMode();
18967         
18968         this.fireEvent('hidepopup', this, this.date);
18969         
18970     },
18971     
18972     onMousedown: function(e)
18973     {
18974         e.stopPropagation();
18975         e.preventDefault();
18976     },
18977     
18978     keyup: function(e)
18979     {
18980         Roo.bootstrap.DateField.superclass.keyup.call(this);
18981         this.update();
18982     },
18983
18984     setValue: function(v)
18985     {
18986         if(this.fireEvent('beforeselect', this, v) !== false){
18987             var d = new Date(this.parseDate(v) ).clearTime();
18988         
18989             if(isNaN(d.getTime())){
18990                 this.date = this.viewDate = '';
18991                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18992                 return;
18993             }
18994
18995             v = this.formatDate(d);
18996
18997             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18998
18999             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19000
19001             this.update();
19002
19003             this.fireEvent('select', this, this.date);
19004         }
19005     },
19006     
19007     getValue: function()
19008     {
19009         return this.formatDate(this.date);
19010     },
19011     
19012     fireKey: function(e)
19013     {
19014         if (!this.picker().isVisible()){
19015             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19016                 this.showPopup();
19017             }
19018             return;
19019         }
19020         
19021         var dateChanged = false,
19022         dir, day, month,
19023         newDate, newViewDate;
19024         
19025         switch(e.keyCode){
19026             case 27: // escape
19027                 this.hidePopup();
19028                 e.preventDefault();
19029                 break;
19030             case 37: // left
19031             case 39: // right
19032                 if (!this.keyboardNavigation) {
19033                     break;
19034                 }
19035                 dir = e.keyCode == 37 ? -1 : 1;
19036                 
19037                 if (e.ctrlKey){
19038                     newDate = this.moveYear(this.date, dir);
19039                     newViewDate = this.moveYear(this.viewDate, dir);
19040                 } else if (e.shiftKey){
19041                     newDate = this.moveMonth(this.date, dir);
19042                     newViewDate = this.moveMonth(this.viewDate, dir);
19043                 } else {
19044                     newDate = new Date(this.date);
19045                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19046                     newViewDate = new Date(this.viewDate);
19047                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19048                 }
19049                 if (this.dateWithinRange(newDate)){
19050                     this.date = newDate;
19051                     this.viewDate = newViewDate;
19052                     this.setValue(this.formatDate(this.date));
19053 //                    this.update();
19054                     e.preventDefault();
19055                     dateChanged = true;
19056                 }
19057                 break;
19058             case 38: // up
19059             case 40: // down
19060                 if (!this.keyboardNavigation) {
19061                     break;
19062                 }
19063                 dir = e.keyCode == 38 ? -1 : 1;
19064                 if (e.ctrlKey){
19065                     newDate = this.moveYear(this.date, dir);
19066                     newViewDate = this.moveYear(this.viewDate, dir);
19067                 } else if (e.shiftKey){
19068                     newDate = this.moveMonth(this.date, dir);
19069                     newViewDate = this.moveMonth(this.viewDate, dir);
19070                 } else {
19071                     newDate = new Date(this.date);
19072                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19073                     newViewDate = new Date(this.viewDate);
19074                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19075                 }
19076                 if (this.dateWithinRange(newDate)){
19077                     this.date = newDate;
19078                     this.viewDate = newViewDate;
19079                     this.setValue(this.formatDate(this.date));
19080 //                    this.update();
19081                     e.preventDefault();
19082                     dateChanged = true;
19083                 }
19084                 break;
19085             case 13: // enter
19086                 this.setValue(this.formatDate(this.date));
19087                 this.hidePopup();
19088                 e.preventDefault();
19089                 break;
19090             case 9: // tab
19091                 this.setValue(this.formatDate(this.date));
19092                 this.hidePopup();
19093                 break;
19094             case 16: // shift
19095             case 17: // ctrl
19096             case 18: // alt
19097                 break;
19098             default :
19099                 this.hide();
19100                 
19101         }
19102     },
19103     
19104     
19105     onClick: function(e) 
19106     {
19107         e.stopPropagation();
19108         e.preventDefault();
19109         
19110         var target = e.getTarget();
19111         
19112         if(target.nodeName.toLowerCase() === 'i'){
19113             target = Roo.get(target).dom.parentNode;
19114         }
19115         
19116         var nodeName = target.nodeName;
19117         var className = target.className;
19118         var html = target.innerHTML;
19119         //Roo.log(nodeName);
19120         
19121         switch(nodeName.toLowerCase()) {
19122             case 'th':
19123                 switch(className) {
19124                     case 'switch':
19125                         this.showMode(1);
19126                         break;
19127                     case 'prev':
19128                     case 'next':
19129                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19130                         switch(this.viewMode){
19131                                 case 0:
19132                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19133                                         break;
19134                                 case 1:
19135                                 case 2:
19136                                         this.viewDate = this.moveYear(this.viewDate, dir);
19137                                         break;
19138                         }
19139                         this.fill();
19140                         break;
19141                     case 'today':
19142                         var date = new Date();
19143                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19144 //                        this.fill()
19145                         this.setValue(this.formatDate(this.date));
19146                         
19147                         this.hidePopup();
19148                         break;
19149                 }
19150                 break;
19151             case 'span':
19152                 if (className.indexOf('disabled') < 0) {
19153                     this.viewDate.setUTCDate(1);
19154                     if (className.indexOf('month') > -1) {
19155                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19156                     } else {
19157                         var year = parseInt(html, 10) || 0;
19158                         this.viewDate.setUTCFullYear(year);
19159                         
19160                     }
19161                     
19162                     if(this.singleMode){
19163                         this.setValue(this.formatDate(this.viewDate));
19164                         this.hidePopup();
19165                         return;
19166                     }
19167                     
19168                     this.showMode(-1);
19169                     this.fill();
19170                 }
19171                 break;
19172                 
19173             case 'td':
19174                 //Roo.log(className);
19175                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19176                     var day = parseInt(html, 10) || 1;
19177                     var year = this.viewDate.getUTCFullYear(),
19178                         month = this.viewDate.getUTCMonth();
19179
19180                     if (className.indexOf('old') > -1) {
19181                         if(month === 0 ){
19182                             month = 11;
19183                             year -= 1;
19184                         }else{
19185                             month -= 1;
19186                         }
19187                     } else if (className.indexOf('new') > -1) {
19188                         if (month == 11) {
19189                             month = 0;
19190                             year += 1;
19191                         } else {
19192                             month += 1;
19193                         }
19194                     }
19195                     //Roo.log([year,month,day]);
19196                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19197                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19198 //                    this.fill();
19199                     //Roo.log(this.formatDate(this.date));
19200                     this.setValue(this.formatDate(this.date));
19201                     this.hidePopup();
19202                 }
19203                 break;
19204         }
19205     },
19206     
19207     setStartDate: function(startDate)
19208     {
19209         this.startDate = startDate || -Infinity;
19210         if (this.startDate !== -Infinity) {
19211             this.startDate = this.parseDate(this.startDate);
19212         }
19213         this.update();
19214         this.updateNavArrows();
19215     },
19216
19217     setEndDate: function(endDate)
19218     {
19219         this.endDate = endDate || Infinity;
19220         if (this.endDate !== Infinity) {
19221             this.endDate = this.parseDate(this.endDate);
19222         }
19223         this.update();
19224         this.updateNavArrows();
19225     },
19226     
19227     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19228     {
19229         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19230         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19231             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19232         }
19233         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19234             return parseInt(d, 10);
19235         });
19236         this.update();
19237         this.updateNavArrows();
19238     },
19239     
19240     updateNavArrows: function() 
19241     {
19242         if(this.singleMode){
19243             return;
19244         }
19245         
19246         var d = new Date(this.viewDate),
19247         year = d.getUTCFullYear(),
19248         month = d.getUTCMonth();
19249         
19250         Roo.each(this.picker().select('.prev', true).elements, function(v){
19251             v.show();
19252             switch (this.viewMode) {
19253                 case 0:
19254
19255                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19256                         v.hide();
19257                     }
19258                     break;
19259                 case 1:
19260                 case 2:
19261                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19262                         v.hide();
19263                     }
19264                     break;
19265             }
19266         });
19267         
19268         Roo.each(this.picker().select('.next', true).elements, function(v){
19269             v.show();
19270             switch (this.viewMode) {
19271                 case 0:
19272
19273                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19274                         v.hide();
19275                     }
19276                     break;
19277                 case 1:
19278                 case 2:
19279                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19280                         v.hide();
19281                     }
19282                     break;
19283             }
19284         })
19285     },
19286     
19287     moveMonth: function(date, dir)
19288     {
19289         if (!dir) {
19290             return date;
19291         }
19292         var new_date = new Date(date.valueOf()),
19293         day = new_date.getUTCDate(),
19294         month = new_date.getUTCMonth(),
19295         mag = Math.abs(dir),
19296         new_month, test;
19297         dir = dir > 0 ? 1 : -1;
19298         if (mag == 1){
19299             test = dir == -1
19300             // If going back one month, make sure month is not current month
19301             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19302             ? function(){
19303                 return new_date.getUTCMonth() == month;
19304             }
19305             // If going forward one month, make sure month is as expected
19306             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19307             : function(){
19308                 return new_date.getUTCMonth() != new_month;
19309             };
19310             new_month = month + dir;
19311             new_date.setUTCMonth(new_month);
19312             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19313             if (new_month < 0 || new_month > 11) {
19314                 new_month = (new_month + 12) % 12;
19315             }
19316         } else {
19317             // For magnitudes >1, move one month at a time...
19318             for (var i=0; i<mag; i++) {
19319                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19320                 new_date = this.moveMonth(new_date, dir);
19321             }
19322             // ...then reset the day, keeping it in the new month
19323             new_month = new_date.getUTCMonth();
19324             new_date.setUTCDate(day);
19325             test = function(){
19326                 return new_month != new_date.getUTCMonth();
19327             };
19328         }
19329         // Common date-resetting loop -- if date is beyond end of month, make it
19330         // end of month
19331         while (test()){
19332             new_date.setUTCDate(--day);
19333             new_date.setUTCMonth(new_month);
19334         }
19335         return new_date;
19336     },
19337
19338     moveYear: function(date, dir)
19339     {
19340         return this.moveMonth(date, dir*12);
19341     },
19342
19343     dateWithinRange: function(date)
19344     {
19345         return date >= this.startDate && date <= this.endDate;
19346     },
19347
19348     
19349     remove: function() 
19350     {
19351         this.picker().remove();
19352     },
19353     
19354     validateValue : function(value)
19355     {
19356         if(this.getVisibilityEl().hasClass('hidden')){
19357             return true;
19358         }
19359         
19360         if(value.length < 1)  {
19361             if(this.allowBlank){
19362                 return true;
19363             }
19364             return false;
19365         }
19366         
19367         if(value.length < this.minLength){
19368             return false;
19369         }
19370         if(value.length > this.maxLength){
19371             return false;
19372         }
19373         if(this.vtype){
19374             var vt = Roo.form.VTypes;
19375             if(!vt[this.vtype](value, this)){
19376                 return false;
19377             }
19378         }
19379         if(typeof this.validator == "function"){
19380             var msg = this.validator(value);
19381             if(msg !== true){
19382                 return false;
19383             }
19384         }
19385         
19386         if(this.regex && !this.regex.test(value)){
19387             return false;
19388         }
19389         
19390         if(typeof(this.parseDate(value)) == 'undefined'){
19391             return false;
19392         }
19393         
19394         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19395             return false;
19396         }      
19397         
19398         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19399             return false;
19400         } 
19401         
19402         
19403         return true;
19404     },
19405     
19406     reset : function()
19407     {
19408         this.date = this.viewDate = '';
19409         
19410         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19411     }
19412    
19413 });
19414
19415 Roo.apply(Roo.bootstrap.DateField,  {
19416     
19417     head : {
19418         tag: 'thead',
19419         cn: [
19420         {
19421             tag: 'tr',
19422             cn: [
19423             {
19424                 tag: 'th',
19425                 cls: 'prev',
19426                 html: '<i class="fa fa-arrow-left"/>'
19427             },
19428             {
19429                 tag: 'th',
19430                 cls: 'switch',
19431                 colspan: '5'
19432             },
19433             {
19434                 tag: 'th',
19435                 cls: 'next',
19436                 html: '<i class="fa fa-arrow-right"/>'
19437             }
19438
19439             ]
19440         }
19441         ]
19442     },
19443     
19444     content : {
19445         tag: 'tbody',
19446         cn: [
19447         {
19448             tag: 'tr',
19449             cn: [
19450             {
19451                 tag: 'td',
19452                 colspan: '7'
19453             }
19454             ]
19455         }
19456         ]
19457     },
19458     
19459     footer : {
19460         tag: 'tfoot',
19461         cn: [
19462         {
19463             tag: 'tr',
19464             cn: [
19465             {
19466                 tag: 'th',
19467                 colspan: '7',
19468                 cls: 'today'
19469             }
19470                     
19471             ]
19472         }
19473         ]
19474     },
19475     
19476     dates:{
19477         en: {
19478             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19479             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19480             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19481             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19482             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19483             today: "Today"
19484         }
19485     },
19486     
19487     modes: [
19488     {
19489         clsName: 'days',
19490         navFnc: 'Month',
19491         navStep: 1
19492     },
19493     {
19494         clsName: 'months',
19495         navFnc: 'FullYear',
19496         navStep: 1
19497     },
19498     {
19499         clsName: 'years',
19500         navFnc: 'FullYear',
19501         navStep: 10
19502     }]
19503 });
19504
19505 Roo.apply(Roo.bootstrap.DateField,  {
19506   
19507     template : {
19508         tag: 'div',
19509         cls: 'datepicker dropdown-menu roo-dynamic',
19510         cn: [
19511         {
19512             tag: 'div',
19513             cls: 'datepicker-days',
19514             cn: [
19515             {
19516                 tag: 'table',
19517                 cls: 'table-condensed',
19518                 cn:[
19519                 Roo.bootstrap.DateField.head,
19520                 {
19521                     tag: 'tbody'
19522                 },
19523                 Roo.bootstrap.DateField.footer
19524                 ]
19525             }
19526             ]
19527         },
19528         {
19529             tag: 'div',
19530             cls: 'datepicker-months',
19531             cn: [
19532             {
19533                 tag: 'table',
19534                 cls: 'table-condensed',
19535                 cn:[
19536                 Roo.bootstrap.DateField.head,
19537                 Roo.bootstrap.DateField.content,
19538                 Roo.bootstrap.DateField.footer
19539                 ]
19540             }
19541             ]
19542         },
19543         {
19544             tag: 'div',
19545             cls: 'datepicker-years',
19546             cn: [
19547             {
19548                 tag: 'table',
19549                 cls: 'table-condensed',
19550                 cn:[
19551                 Roo.bootstrap.DateField.head,
19552                 Roo.bootstrap.DateField.content,
19553                 Roo.bootstrap.DateField.footer
19554                 ]
19555             }
19556             ]
19557         }
19558         ]
19559     }
19560 });
19561
19562  
19563
19564  /*
19565  * - LGPL
19566  *
19567  * TimeField
19568  * 
19569  */
19570
19571 /**
19572  * @class Roo.bootstrap.TimeField
19573  * @extends Roo.bootstrap.Input
19574  * Bootstrap DateField class
19575  * 
19576  * 
19577  * @constructor
19578  * Create a new TimeField
19579  * @param {Object} config The config object
19580  */
19581
19582 Roo.bootstrap.TimeField = function(config){
19583     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19584     this.addEvents({
19585             /**
19586              * @event show
19587              * Fires when this field show.
19588              * @param {Roo.bootstrap.DateField} thisthis
19589              * @param {Mixed} date The date value
19590              */
19591             show : true,
19592             /**
19593              * @event show
19594              * Fires when this field hide.
19595              * @param {Roo.bootstrap.DateField} this
19596              * @param {Mixed} date The date value
19597              */
19598             hide : true,
19599             /**
19600              * @event select
19601              * Fires when select a date.
19602              * @param {Roo.bootstrap.DateField} this
19603              * @param {Mixed} date The date value
19604              */
19605             select : true
19606         });
19607 };
19608
19609 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19610     
19611     /**
19612      * @cfg {String} format
19613      * The default time format string which can be overriden for localization support.  The format must be
19614      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19615      */
19616     format : "H:i",
19617        
19618     onRender: function(ct, position)
19619     {
19620         
19621         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19622                 
19623         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19624         
19625         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19626         
19627         this.pop = this.picker().select('>.datepicker-time',true).first();
19628         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19629         
19630         this.picker().on('mousedown', this.onMousedown, this);
19631         this.picker().on('click', this.onClick, this);
19632         
19633         this.picker().addClass('datepicker-dropdown');
19634     
19635         this.fillTime();
19636         this.update();
19637             
19638         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19639         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19640         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19641         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19642         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19643         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19644
19645     },
19646     
19647     fireKey: function(e){
19648         if (!this.picker().isVisible()){
19649             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19650                 this.show();
19651             }
19652             return;
19653         }
19654
19655         e.preventDefault();
19656         
19657         switch(e.keyCode){
19658             case 27: // escape
19659                 this.hide();
19660                 break;
19661             case 37: // left
19662             case 39: // right
19663                 this.onTogglePeriod();
19664                 break;
19665             case 38: // up
19666                 this.onIncrementMinutes();
19667                 break;
19668             case 40: // down
19669                 this.onDecrementMinutes();
19670                 break;
19671             case 13: // enter
19672             case 9: // tab
19673                 this.setTime();
19674                 break;
19675         }
19676     },
19677     
19678     onClick: function(e) {
19679         e.stopPropagation();
19680         e.preventDefault();
19681     },
19682     
19683     picker : function()
19684     {
19685         return this.el.select('.datepicker', true).first();
19686     },
19687     
19688     fillTime: function()
19689     {    
19690         var time = this.pop.select('tbody', true).first();
19691         
19692         time.dom.innerHTML = '';
19693         
19694         time.createChild({
19695             tag: 'tr',
19696             cn: [
19697                 {
19698                     tag: 'td',
19699                     cn: [
19700                         {
19701                             tag: 'a',
19702                             href: '#',
19703                             cls: 'btn',
19704                             cn: [
19705                                 {
19706                                     tag: 'span',
19707                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19708                                 }
19709                             ]
19710                         } 
19711                     ]
19712                 },
19713                 {
19714                     tag: 'td',
19715                     cls: 'separator'
19716                 },
19717                 {
19718                     tag: 'td',
19719                     cn: [
19720                         {
19721                             tag: 'a',
19722                             href: '#',
19723                             cls: 'btn',
19724                             cn: [
19725                                 {
19726                                     tag: 'span',
19727                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19728                                 }
19729                             ]
19730                         }
19731                     ]
19732                 },
19733                 {
19734                     tag: 'td',
19735                     cls: 'separator'
19736                 }
19737             ]
19738         });
19739         
19740         time.createChild({
19741             tag: 'tr',
19742             cn: [
19743                 {
19744                     tag: 'td',
19745                     cn: [
19746                         {
19747                             tag: 'span',
19748                             cls: 'timepicker-hour',
19749                             html: '00'
19750                         }  
19751                     ]
19752                 },
19753                 {
19754                     tag: 'td',
19755                     cls: 'separator',
19756                     html: ':'
19757                 },
19758                 {
19759                     tag: 'td',
19760                     cn: [
19761                         {
19762                             tag: 'span',
19763                             cls: 'timepicker-minute',
19764                             html: '00'
19765                         }  
19766                     ]
19767                 },
19768                 {
19769                     tag: 'td',
19770                     cls: 'separator'
19771                 },
19772                 {
19773                     tag: 'td',
19774                     cn: [
19775                         {
19776                             tag: 'button',
19777                             type: 'button',
19778                             cls: 'btn btn-primary period',
19779                             html: 'AM'
19780                             
19781                         }
19782                     ]
19783                 }
19784             ]
19785         });
19786         
19787         time.createChild({
19788             tag: 'tr',
19789             cn: [
19790                 {
19791                     tag: 'td',
19792                     cn: [
19793                         {
19794                             tag: 'a',
19795                             href: '#',
19796                             cls: 'btn',
19797                             cn: [
19798                                 {
19799                                     tag: 'span',
19800                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19801                                 }
19802                             ]
19803                         }
19804                     ]
19805                 },
19806                 {
19807                     tag: 'td',
19808                     cls: 'separator'
19809                 },
19810                 {
19811                     tag: 'td',
19812                     cn: [
19813                         {
19814                             tag: 'a',
19815                             href: '#',
19816                             cls: 'btn',
19817                             cn: [
19818                                 {
19819                                     tag: 'span',
19820                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19821                                 }
19822                             ]
19823                         }
19824                     ]
19825                 },
19826                 {
19827                     tag: 'td',
19828                     cls: 'separator'
19829                 }
19830             ]
19831         });
19832         
19833     },
19834     
19835     update: function()
19836     {
19837         
19838         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19839         
19840         this.fill();
19841     },
19842     
19843     fill: function() 
19844     {
19845         var hours = this.time.getHours();
19846         var minutes = this.time.getMinutes();
19847         var period = 'AM';
19848         
19849         if(hours > 11){
19850             period = 'PM';
19851         }
19852         
19853         if(hours == 0){
19854             hours = 12;
19855         }
19856         
19857         
19858         if(hours > 12){
19859             hours = hours - 12;
19860         }
19861         
19862         if(hours < 10){
19863             hours = '0' + hours;
19864         }
19865         
19866         if(minutes < 10){
19867             minutes = '0' + minutes;
19868         }
19869         
19870         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19871         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19872         this.pop.select('button', true).first().dom.innerHTML = period;
19873         
19874     },
19875     
19876     place: function()
19877     {   
19878         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19879         
19880         var cls = ['bottom'];
19881         
19882         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19883             cls.pop();
19884             cls.push('top');
19885         }
19886         
19887         cls.push('right');
19888         
19889         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19890             cls.pop();
19891             cls.push('left');
19892         }
19893         
19894         this.picker().addClass(cls.join('-'));
19895         
19896         var _this = this;
19897         
19898         Roo.each(cls, function(c){
19899             if(c == 'bottom'){
19900                 _this.picker().setTop(_this.inputEl().getHeight());
19901                 return;
19902             }
19903             if(c == 'top'){
19904                 _this.picker().setTop(0 - _this.picker().getHeight());
19905                 return;
19906             }
19907             
19908             if(c == 'left'){
19909                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19910                 return;
19911             }
19912             if(c == 'right'){
19913                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19914                 return;
19915             }
19916         });
19917         
19918     },
19919   
19920     onFocus : function()
19921     {
19922         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19923         this.show();
19924     },
19925     
19926     onBlur : function()
19927     {
19928         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19929         this.hide();
19930     },
19931     
19932     show : function()
19933     {
19934         this.picker().show();
19935         this.pop.show();
19936         this.update();
19937         this.place();
19938         
19939         this.fireEvent('show', this, this.date);
19940     },
19941     
19942     hide : function()
19943     {
19944         this.picker().hide();
19945         this.pop.hide();
19946         
19947         this.fireEvent('hide', this, this.date);
19948     },
19949     
19950     setTime : function()
19951     {
19952         this.hide();
19953         this.setValue(this.time.format(this.format));
19954         
19955         this.fireEvent('select', this, this.date);
19956         
19957         
19958     },
19959     
19960     onMousedown: function(e){
19961         e.stopPropagation();
19962         e.preventDefault();
19963     },
19964     
19965     onIncrementHours: function()
19966     {
19967         Roo.log('onIncrementHours');
19968         this.time = this.time.add(Date.HOUR, 1);
19969         this.update();
19970         
19971     },
19972     
19973     onDecrementHours: function()
19974     {
19975         Roo.log('onDecrementHours');
19976         this.time = this.time.add(Date.HOUR, -1);
19977         this.update();
19978     },
19979     
19980     onIncrementMinutes: function()
19981     {
19982         Roo.log('onIncrementMinutes');
19983         this.time = this.time.add(Date.MINUTE, 1);
19984         this.update();
19985     },
19986     
19987     onDecrementMinutes: function()
19988     {
19989         Roo.log('onDecrementMinutes');
19990         this.time = this.time.add(Date.MINUTE, -1);
19991         this.update();
19992     },
19993     
19994     onTogglePeriod: function()
19995     {
19996         Roo.log('onTogglePeriod');
19997         this.time = this.time.add(Date.HOUR, 12);
19998         this.update();
19999     }
20000     
20001    
20002 });
20003
20004 Roo.apply(Roo.bootstrap.TimeField,  {
20005     
20006     content : {
20007         tag: 'tbody',
20008         cn: [
20009             {
20010                 tag: 'tr',
20011                 cn: [
20012                 {
20013                     tag: 'td',
20014                     colspan: '7'
20015                 }
20016                 ]
20017             }
20018         ]
20019     },
20020     
20021     footer : {
20022         tag: 'tfoot',
20023         cn: [
20024             {
20025                 tag: 'tr',
20026                 cn: [
20027                 {
20028                     tag: 'th',
20029                     colspan: '7',
20030                     cls: '',
20031                     cn: [
20032                         {
20033                             tag: 'button',
20034                             cls: 'btn btn-info ok',
20035                             html: 'OK'
20036                         }
20037                     ]
20038                 }
20039
20040                 ]
20041             }
20042         ]
20043     }
20044 });
20045
20046 Roo.apply(Roo.bootstrap.TimeField,  {
20047   
20048     template : {
20049         tag: 'div',
20050         cls: 'datepicker dropdown-menu',
20051         cn: [
20052             {
20053                 tag: 'div',
20054                 cls: 'datepicker-time',
20055                 cn: [
20056                 {
20057                     tag: 'table',
20058                     cls: 'table-condensed',
20059                     cn:[
20060                     Roo.bootstrap.TimeField.content,
20061                     Roo.bootstrap.TimeField.footer
20062                     ]
20063                 }
20064                 ]
20065             }
20066         ]
20067     }
20068 });
20069
20070  
20071
20072  /*
20073  * - LGPL
20074  *
20075  * MonthField
20076  * 
20077  */
20078
20079 /**
20080  * @class Roo.bootstrap.MonthField
20081  * @extends Roo.bootstrap.Input
20082  * Bootstrap MonthField class
20083  * 
20084  * @cfg {String} language default en
20085  * 
20086  * @constructor
20087  * Create a new MonthField
20088  * @param {Object} config The config object
20089  */
20090
20091 Roo.bootstrap.MonthField = function(config){
20092     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20093     
20094     this.addEvents({
20095         /**
20096          * @event show
20097          * Fires when this field show.
20098          * @param {Roo.bootstrap.MonthField} this
20099          * @param {Mixed} date The date value
20100          */
20101         show : true,
20102         /**
20103          * @event show
20104          * Fires when this field hide.
20105          * @param {Roo.bootstrap.MonthField} this
20106          * @param {Mixed} date The date value
20107          */
20108         hide : true,
20109         /**
20110          * @event select
20111          * Fires when select a date.
20112          * @param {Roo.bootstrap.MonthField} this
20113          * @param {String} oldvalue The old value
20114          * @param {String} newvalue The new value
20115          */
20116         select : true
20117     });
20118 };
20119
20120 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20121     
20122     onRender: function(ct, position)
20123     {
20124         
20125         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20126         
20127         this.language = this.language || 'en';
20128         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20129         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20130         
20131         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20132         this.isInline = false;
20133         this.isInput = true;
20134         this.component = this.el.select('.add-on', true).first() || false;
20135         this.component = (this.component && this.component.length === 0) ? false : this.component;
20136         this.hasInput = this.component && this.inputEL().length;
20137         
20138         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20139         
20140         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20141         
20142         this.picker().on('mousedown', this.onMousedown, this);
20143         this.picker().on('click', this.onClick, this);
20144         
20145         this.picker().addClass('datepicker-dropdown');
20146         
20147         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20148             v.setStyle('width', '189px');
20149         });
20150         
20151         this.fillMonths();
20152         
20153         this.update();
20154         
20155         if(this.isInline) {
20156             this.show();
20157         }
20158         
20159     },
20160     
20161     setValue: function(v, suppressEvent)
20162     {   
20163         var o = this.getValue();
20164         
20165         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20166         
20167         this.update();
20168
20169         if(suppressEvent !== true){
20170             this.fireEvent('select', this, o, v);
20171         }
20172         
20173     },
20174     
20175     getValue: function()
20176     {
20177         return this.value;
20178     },
20179     
20180     onClick: function(e) 
20181     {
20182         e.stopPropagation();
20183         e.preventDefault();
20184         
20185         var target = e.getTarget();
20186         
20187         if(target.nodeName.toLowerCase() === 'i'){
20188             target = Roo.get(target).dom.parentNode;
20189         }
20190         
20191         var nodeName = target.nodeName;
20192         var className = target.className;
20193         var html = target.innerHTML;
20194         
20195         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20196             return;
20197         }
20198         
20199         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20200         
20201         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20202         
20203         this.hide();
20204                         
20205     },
20206     
20207     picker : function()
20208     {
20209         return this.pickerEl;
20210     },
20211     
20212     fillMonths: function()
20213     {    
20214         var i = 0;
20215         var months = this.picker().select('>.datepicker-months td', true).first();
20216         
20217         months.dom.innerHTML = '';
20218         
20219         while (i < 12) {
20220             var month = {
20221                 tag: 'span',
20222                 cls: 'month',
20223                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20224             };
20225             
20226             months.createChild(month);
20227         }
20228         
20229     },
20230     
20231     update: function()
20232     {
20233         var _this = this;
20234         
20235         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20236             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20237         }
20238         
20239         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20240             e.removeClass('active');
20241             
20242             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20243                 e.addClass('active');
20244             }
20245         })
20246     },
20247     
20248     place: function()
20249     {
20250         if(this.isInline) {
20251             return;
20252         }
20253         
20254         this.picker().removeClass(['bottom', 'top']);
20255         
20256         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20257             /*
20258              * place to the top of element!
20259              *
20260              */
20261             
20262             this.picker().addClass('top');
20263             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20264             
20265             return;
20266         }
20267         
20268         this.picker().addClass('bottom');
20269         
20270         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20271     },
20272     
20273     onFocus : function()
20274     {
20275         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20276         this.show();
20277     },
20278     
20279     onBlur : function()
20280     {
20281         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20282         
20283         var d = this.inputEl().getValue();
20284         
20285         this.setValue(d);
20286                 
20287         this.hide();
20288     },
20289     
20290     show : function()
20291     {
20292         this.picker().show();
20293         this.picker().select('>.datepicker-months', true).first().show();
20294         this.update();
20295         this.place();
20296         
20297         this.fireEvent('show', this, this.date);
20298     },
20299     
20300     hide : function()
20301     {
20302         if(this.isInline) {
20303             return;
20304         }
20305         this.picker().hide();
20306         this.fireEvent('hide', this, this.date);
20307         
20308     },
20309     
20310     onMousedown: function(e)
20311     {
20312         e.stopPropagation();
20313         e.preventDefault();
20314     },
20315     
20316     keyup: function(e)
20317     {
20318         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20319         this.update();
20320     },
20321
20322     fireKey: function(e)
20323     {
20324         if (!this.picker().isVisible()){
20325             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20326                 this.show();
20327             }
20328             return;
20329         }
20330         
20331         var dir;
20332         
20333         switch(e.keyCode){
20334             case 27: // escape
20335                 this.hide();
20336                 e.preventDefault();
20337                 break;
20338             case 37: // left
20339             case 39: // right
20340                 dir = e.keyCode == 37 ? -1 : 1;
20341                 
20342                 this.vIndex = this.vIndex + dir;
20343                 
20344                 if(this.vIndex < 0){
20345                     this.vIndex = 0;
20346                 }
20347                 
20348                 if(this.vIndex > 11){
20349                     this.vIndex = 11;
20350                 }
20351                 
20352                 if(isNaN(this.vIndex)){
20353                     this.vIndex = 0;
20354                 }
20355                 
20356                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20357                 
20358                 break;
20359             case 38: // up
20360             case 40: // down
20361                 
20362                 dir = e.keyCode == 38 ? -1 : 1;
20363                 
20364                 this.vIndex = this.vIndex + dir * 4;
20365                 
20366                 if(this.vIndex < 0){
20367                     this.vIndex = 0;
20368                 }
20369                 
20370                 if(this.vIndex > 11){
20371                     this.vIndex = 11;
20372                 }
20373                 
20374                 if(isNaN(this.vIndex)){
20375                     this.vIndex = 0;
20376                 }
20377                 
20378                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20379                 break;
20380                 
20381             case 13: // enter
20382                 
20383                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20384                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20385                 }
20386                 
20387                 this.hide();
20388                 e.preventDefault();
20389                 break;
20390             case 9: // tab
20391                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20392                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20393                 }
20394                 this.hide();
20395                 break;
20396             case 16: // shift
20397             case 17: // ctrl
20398             case 18: // alt
20399                 break;
20400             default :
20401                 this.hide();
20402                 
20403         }
20404     },
20405     
20406     remove: function() 
20407     {
20408         this.picker().remove();
20409     }
20410    
20411 });
20412
20413 Roo.apply(Roo.bootstrap.MonthField,  {
20414     
20415     content : {
20416         tag: 'tbody',
20417         cn: [
20418         {
20419             tag: 'tr',
20420             cn: [
20421             {
20422                 tag: 'td',
20423                 colspan: '7'
20424             }
20425             ]
20426         }
20427         ]
20428     },
20429     
20430     dates:{
20431         en: {
20432             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20433             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20434         }
20435     }
20436 });
20437
20438 Roo.apply(Roo.bootstrap.MonthField,  {
20439   
20440     template : {
20441         tag: 'div',
20442         cls: 'datepicker dropdown-menu roo-dynamic',
20443         cn: [
20444             {
20445                 tag: 'div',
20446                 cls: 'datepicker-months',
20447                 cn: [
20448                 {
20449                     tag: 'table',
20450                     cls: 'table-condensed',
20451                     cn:[
20452                         Roo.bootstrap.DateField.content
20453                     ]
20454                 }
20455                 ]
20456             }
20457         ]
20458     }
20459 });
20460
20461  
20462
20463  
20464  /*
20465  * - LGPL
20466  *
20467  * CheckBox
20468  * 
20469  */
20470
20471 /**
20472  * @class Roo.bootstrap.CheckBox
20473  * @extends Roo.bootstrap.Input
20474  * Bootstrap CheckBox class
20475  * 
20476  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20477  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20478  * @cfg {String} boxLabel The text that appears beside the checkbox
20479  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20480  * @cfg {Boolean} checked initnal the element
20481  * @cfg {Boolean} inline inline the element (default false)
20482  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20483  * @cfg {String} tooltip label tooltip
20484  * 
20485  * @constructor
20486  * Create a new CheckBox
20487  * @param {Object} config The config object
20488  */
20489
20490 Roo.bootstrap.CheckBox = function(config){
20491     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20492    
20493     this.addEvents({
20494         /**
20495         * @event check
20496         * Fires when the element is checked or unchecked.
20497         * @param {Roo.bootstrap.CheckBox} this This input
20498         * @param {Boolean} checked The new checked value
20499         */
20500        check : true,
20501        /**
20502         * @event click
20503         * Fires when the element is click.
20504         * @param {Roo.bootstrap.CheckBox} this This input
20505         */
20506        click : true
20507     });
20508     
20509 };
20510
20511 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20512   
20513     inputType: 'checkbox',
20514     inputValue: 1,
20515     valueOff: 0,
20516     boxLabel: false,
20517     checked: false,
20518     weight : false,
20519     inline: false,
20520     tooltip : '',
20521     
20522     getAutoCreate : function()
20523     {
20524         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20525         
20526         var id = Roo.id();
20527         
20528         var cfg = {};
20529         
20530         cfg.cls = 'form-group ' + this.inputType; //input-group
20531         
20532         if(this.inline){
20533             cfg.cls += ' ' + this.inputType + '-inline';
20534         }
20535         
20536         var input =  {
20537             tag: 'input',
20538             id : id,
20539             type : this.inputType,
20540             value : this.inputValue,
20541             cls : 'roo-' + this.inputType, //'form-box',
20542             placeholder : this.placeholder || ''
20543             
20544         };
20545         
20546         if(this.inputType != 'radio'){
20547             var hidden =  {
20548                 tag: 'input',
20549                 type : 'hidden',
20550                 cls : 'roo-hidden-value',
20551                 value : this.checked ? this.inputValue : this.valueOff
20552             };
20553         }
20554         
20555             
20556         if (this.weight) { // Validity check?
20557             cfg.cls += " " + this.inputType + "-" + this.weight;
20558         }
20559         
20560         if (this.disabled) {
20561             input.disabled=true;
20562         }
20563         
20564         if(this.checked){
20565             input.checked = this.checked;
20566         }
20567         
20568         if (this.name) {
20569             
20570             input.name = this.name;
20571             
20572             if(this.inputType != 'radio'){
20573                 hidden.name = this.name;
20574                 input.name = '_hidden_' + this.name;
20575             }
20576         }
20577         
20578         if (this.size) {
20579             input.cls += ' input-' + this.size;
20580         }
20581         
20582         var settings=this;
20583         
20584         ['xs','sm','md','lg'].map(function(size){
20585             if (settings[size]) {
20586                 cfg.cls += ' col-' + size + '-' + settings[size];
20587             }
20588         });
20589         
20590         var inputblock = input;
20591          
20592         if (this.before || this.after) {
20593             
20594             inputblock = {
20595                 cls : 'input-group',
20596                 cn :  [] 
20597             };
20598             
20599             if (this.before) {
20600                 inputblock.cn.push({
20601                     tag :'span',
20602                     cls : 'input-group-addon',
20603                     html : this.before
20604                 });
20605             }
20606             
20607             inputblock.cn.push(input);
20608             
20609             if(this.inputType != 'radio'){
20610                 inputblock.cn.push(hidden);
20611             }
20612             
20613             if (this.after) {
20614                 inputblock.cn.push({
20615                     tag :'span',
20616                     cls : 'input-group-addon',
20617                     html : this.after
20618                 });
20619             }
20620             
20621         }
20622         
20623         if (align ==='left' && this.fieldLabel.length) {
20624 //                Roo.log("left and has label");
20625             cfg.cn = [
20626                 {
20627                     tag: 'label',
20628                     'for' :  id,
20629                     cls : 'control-label',
20630                     html : this.fieldLabel
20631                 },
20632                 {
20633                     cls : "", 
20634                     cn: [
20635                         inputblock
20636                     ]
20637                 }
20638             ];
20639             
20640             if(this.labelWidth > 12){
20641                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20642             }
20643             
20644             if(this.labelWidth < 13 && this.labelmd == 0){
20645                 this.labelmd = this.labelWidth;
20646             }
20647             
20648             if(this.labellg > 0){
20649                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20650                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20651             }
20652             
20653             if(this.labelmd > 0){
20654                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20655                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20656             }
20657             
20658             if(this.labelsm > 0){
20659                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20660                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20661             }
20662             
20663             if(this.labelxs > 0){
20664                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20665                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20666             }
20667             
20668         } else if ( this.fieldLabel.length) {
20669 //                Roo.log(" label");
20670                 cfg.cn = [
20671                    
20672                     {
20673                         tag: this.boxLabel ? 'span' : 'label',
20674                         'for': id,
20675                         cls: 'control-label box-input-label',
20676                         //cls : 'input-group-addon',
20677                         html : this.fieldLabel
20678                     },
20679                     
20680                     inputblock
20681                     
20682                 ];
20683
20684         } else {
20685             
20686 //                Roo.log(" no label && no align");
20687                 cfg.cn = [  inputblock ] ;
20688                 
20689                 
20690         }
20691         
20692         if(this.boxLabel){
20693              var boxLabelCfg = {
20694                 tag: 'label',
20695                 //'for': id, // box label is handled by onclick - so no for...
20696                 cls: 'box-label',
20697                 html: this.boxLabel
20698             };
20699             
20700             if(this.tooltip){
20701                 boxLabelCfg.tooltip = this.tooltip;
20702             }
20703              
20704             cfg.cn.push(boxLabelCfg);
20705         }
20706         
20707         if(this.inputType != 'radio'){
20708             cfg.cn.push(hidden);
20709         }
20710         
20711         return cfg;
20712         
20713     },
20714     
20715     /**
20716      * return the real input element.
20717      */
20718     inputEl: function ()
20719     {
20720         return this.el.select('input.roo-' + this.inputType,true).first();
20721     },
20722     hiddenEl: function ()
20723     {
20724         return this.el.select('input.roo-hidden-value',true).first();
20725     },
20726     
20727     labelEl: function()
20728     {
20729         return this.el.select('label.control-label',true).first();
20730     },
20731     /* depricated... */
20732     
20733     label: function()
20734     {
20735         return this.labelEl();
20736     },
20737     
20738     boxLabelEl: function()
20739     {
20740         return this.el.select('label.box-label',true).first();
20741     },
20742     
20743     initEvents : function()
20744     {
20745 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20746         
20747         this.inputEl().on('click', this.onClick,  this);
20748         
20749         if (this.boxLabel) { 
20750             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20751         }
20752         
20753         this.startValue = this.getValue();
20754         
20755         if(this.groupId){
20756             Roo.bootstrap.CheckBox.register(this);
20757         }
20758     },
20759     
20760     onClick : function(e)
20761     {   
20762         if(this.fireEvent('click', this, e) !== false){
20763             this.setChecked(!this.checked);
20764         }
20765         
20766     },
20767     
20768     setChecked : function(state,suppressEvent)
20769     {
20770         this.startValue = this.getValue();
20771
20772         if(this.inputType == 'radio'){
20773             
20774             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20775                 e.dom.checked = false;
20776             });
20777             
20778             this.inputEl().dom.checked = true;
20779             
20780             this.inputEl().dom.value = this.inputValue;
20781             
20782             if(suppressEvent !== true){
20783                 this.fireEvent('check', this, true);
20784             }
20785             
20786             this.validate();
20787             
20788             return;
20789         }
20790         
20791         this.checked = state;
20792         
20793         this.inputEl().dom.checked = state;
20794         
20795         
20796         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20797         
20798         if(suppressEvent !== true){
20799             this.fireEvent('check', this, state);
20800         }
20801         
20802         this.validate();
20803     },
20804     
20805     getValue : function()
20806     {
20807         if(this.inputType == 'radio'){
20808             return this.getGroupValue();
20809         }
20810         
20811         return this.hiddenEl().dom.value;
20812         
20813     },
20814     
20815     getGroupValue : function()
20816     {
20817         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20818             return '';
20819         }
20820         
20821         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20822     },
20823     
20824     setValue : function(v,suppressEvent)
20825     {
20826         if(this.inputType == 'radio'){
20827             this.setGroupValue(v, suppressEvent);
20828             return;
20829         }
20830         
20831         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20832         
20833         this.validate();
20834     },
20835     
20836     setGroupValue : function(v, suppressEvent)
20837     {
20838         this.startValue = this.getValue();
20839         
20840         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20841             e.dom.checked = false;
20842             
20843             if(e.dom.value == v){
20844                 e.dom.checked = true;
20845             }
20846         });
20847         
20848         if(suppressEvent !== true){
20849             this.fireEvent('check', this, true);
20850         }
20851
20852         this.validate();
20853         
20854         return;
20855     },
20856     
20857     validate : function()
20858     {
20859         if(this.getVisibilityEl().hasClass('hidden')){
20860             return true;
20861         }
20862         
20863         if(
20864                 this.disabled || 
20865                 (this.inputType == 'radio' && this.validateRadio()) ||
20866                 (this.inputType == 'checkbox' && this.validateCheckbox())
20867         ){
20868             this.markValid();
20869             return true;
20870         }
20871         
20872         this.markInvalid();
20873         return false;
20874     },
20875     
20876     validateRadio : function()
20877     {
20878         if(this.getVisibilityEl().hasClass('hidden')){
20879             return true;
20880         }
20881         
20882         if(this.allowBlank){
20883             return true;
20884         }
20885         
20886         var valid = false;
20887         
20888         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20889             if(!e.dom.checked){
20890                 return;
20891             }
20892             
20893             valid = true;
20894             
20895             return false;
20896         });
20897         
20898         return valid;
20899     },
20900     
20901     validateCheckbox : function()
20902     {
20903         if(!this.groupId){
20904             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20905             //return (this.getValue() == this.inputValue) ? true : false;
20906         }
20907         
20908         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20909         
20910         if(!group){
20911             return false;
20912         }
20913         
20914         var r = false;
20915         
20916         for(var i in group){
20917             if(group[i].el.isVisible(true)){
20918                 r = false;
20919                 break;
20920             }
20921             
20922             r = true;
20923         }
20924         
20925         for(var i in group){
20926             if(r){
20927                 break;
20928             }
20929             
20930             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20931         }
20932         
20933         return r;
20934     },
20935     
20936     /**
20937      * Mark this field as valid
20938      */
20939     markValid : function()
20940     {
20941         var _this = this;
20942         
20943         this.fireEvent('valid', this);
20944         
20945         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20946         
20947         if(this.groupId){
20948             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20949         }
20950         
20951         if(label){
20952             label.markValid();
20953         }
20954
20955         if(this.inputType == 'radio'){
20956             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20957                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20958                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20959             });
20960             
20961             return;
20962         }
20963
20964         if(!this.groupId){
20965             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20966             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20967             return;
20968         }
20969         
20970         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20971         
20972         if(!group){
20973             return;
20974         }
20975         
20976         for(var i in group){
20977             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20978             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20979         }
20980     },
20981     
20982      /**
20983      * Mark this field as invalid
20984      * @param {String} msg The validation message
20985      */
20986     markInvalid : function(msg)
20987     {
20988         if(this.allowBlank){
20989             return;
20990         }
20991         
20992         var _this = this;
20993         
20994         this.fireEvent('invalid', this, msg);
20995         
20996         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20997         
20998         if(this.groupId){
20999             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21000         }
21001         
21002         if(label){
21003             label.markInvalid();
21004         }
21005             
21006         if(this.inputType == 'radio'){
21007             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21008                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21009                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21010             });
21011             
21012             return;
21013         }
21014         
21015         if(!this.groupId){
21016             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21017             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21018             return;
21019         }
21020         
21021         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21022         
21023         if(!group){
21024             return;
21025         }
21026         
21027         for(var i in group){
21028             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21029             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21030         }
21031         
21032     },
21033     
21034     clearInvalid : function()
21035     {
21036         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21037         
21038         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21039         
21040         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21041         
21042         if (label && label.iconEl) {
21043             label.iconEl.removeClass(label.validClass);
21044             label.iconEl.removeClass(label.invalidClass);
21045         }
21046     },
21047     
21048     disable : function()
21049     {
21050         if(this.inputType != 'radio'){
21051             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21052             return;
21053         }
21054         
21055         var _this = this;
21056         
21057         if(this.rendered){
21058             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21059                 _this.getActionEl().addClass(this.disabledClass);
21060                 e.dom.disabled = true;
21061             });
21062         }
21063         
21064         this.disabled = true;
21065         this.fireEvent("disable", this);
21066         return this;
21067     },
21068
21069     enable : function()
21070     {
21071         if(this.inputType != 'radio'){
21072             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21073             return;
21074         }
21075         
21076         var _this = this;
21077         
21078         if(this.rendered){
21079             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21080                 _this.getActionEl().removeClass(this.disabledClass);
21081                 e.dom.disabled = false;
21082             });
21083         }
21084         
21085         this.disabled = false;
21086         this.fireEvent("enable", this);
21087         return this;
21088     },
21089     
21090     setBoxLabel : function(v)
21091     {
21092         this.boxLabel = v;
21093         
21094         if(this.rendered){
21095             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21096         }
21097     }
21098
21099 });
21100
21101 Roo.apply(Roo.bootstrap.CheckBox, {
21102     
21103     groups: {},
21104     
21105      /**
21106     * register a CheckBox Group
21107     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21108     */
21109     register : function(checkbox)
21110     {
21111         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21112             this.groups[checkbox.groupId] = {};
21113         }
21114         
21115         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21116             return;
21117         }
21118         
21119         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21120         
21121     },
21122     /**
21123     * fetch a CheckBox Group based on the group ID
21124     * @param {string} the group ID
21125     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21126     */
21127     get: function(groupId) {
21128         if (typeof(this.groups[groupId]) == 'undefined') {
21129             return false;
21130         }
21131         
21132         return this.groups[groupId] ;
21133     }
21134     
21135     
21136 });
21137 /*
21138  * - LGPL
21139  *
21140  * RadioItem
21141  * 
21142  */
21143
21144 /**
21145  * @class Roo.bootstrap.Radio
21146  * @extends Roo.bootstrap.Component
21147  * Bootstrap Radio class
21148  * @cfg {String} boxLabel - the label associated
21149  * @cfg {String} value - the value of radio
21150  * 
21151  * @constructor
21152  * Create a new Radio
21153  * @param {Object} config The config object
21154  */
21155 Roo.bootstrap.Radio = function(config){
21156     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21157     
21158 };
21159
21160 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21161     
21162     boxLabel : '',
21163     
21164     value : '',
21165     
21166     getAutoCreate : function()
21167     {
21168         var cfg = {
21169             tag : 'div',
21170             cls : 'form-group radio',
21171             cn : [
21172                 {
21173                     tag : 'label',
21174                     cls : 'box-label',
21175                     html : this.boxLabel
21176                 }
21177             ]
21178         };
21179         
21180         return cfg;
21181     },
21182     
21183     initEvents : function() 
21184     {
21185         this.parent().register(this);
21186         
21187         this.el.on('click', this.onClick, this);
21188         
21189     },
21190     
21191     onClick : function(e)
21192     {
21193         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21194             this.setChecked(true);
21195         }
21196     },
21197     
21198     setChecked : function(state, suppressEvent)
21199     {
21200         this.parent().setValue(this.value, suppressEvent);
21201         
21202     },
21203     
21204     setBoxLabel : function(v)
21205     {
21206         this.boxLabel = v;
21207         
21208         if(this.rendered){
21209             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21210         }
21211     }
21212     
21213 });
21214  
21215
21216  /*
21217  * - LGPL
21218  *
21219  * Input
21220  * 
21221  */
21222
21223 /**
21224  * @class Roo.bootstrap.SecurePass
21225  * @extends Roo.bootstrap.Input
21226  * Bootstrap SecurePass class
21227  *
21228  * 
21229  * @constructor
21230  * Create a new SecurePass
21231  * @param {Object} config The config object
21232  */
21233  
21234 Roo.bootstrap.SecurePass = function (config) {
21235     // these go here, so the translation tool can replace them..
21236     this.errors = {
21237         PwdEmpty: "Please type a password, and then retype it to confirm.",
21238         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21239         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21240         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21241         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21242         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21243         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21244         TooWeak: "Your password is Too Weak."
21245     },
21246     this.meterLabel = "Password strength:";
21247     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21248     this.meterClass = [
21249         "roo-password-meter-tooweak", 
21250         "roo-password-meter-weak", 
21251         "roo-password-meter-medium", 
21252         "roo-password-meter-strong", 
21253         "roo-password-meter-grey"
21254     ];
21255     
21256     this.errors = {};
21257     
21258     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21259 }
21260
21261 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21262     /**
21263      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21264      * {
21265      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21266      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21267      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21268      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21269      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21270      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21271      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21272      * })
21273      */
21274     // private
21275     
21276     meterWidth: 300,
21277     errorMsg :'',    
21278     errors: false,
21279     imageRoot: '/',
21280     /**
21281      * @cfg {String/Object} Label for the strength meter (defaults to
21282      * 'Password strength:')
21283      */
21284     // private
21285     meterLabel: '',
21286     /**
21287      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21288      * ['Weak', 'Medium', 'Strong'])
21289      */
21290     // private    
21291     pwdStrengths: false,    
21292     // private
21293     strength: 0,
21294     // private
21295     _lastPwd: null,
21296     // private
21297     kCapitalLetter: 0,
21298     kSmallLetter: 1,
21299     kDigit: 2,
21300     kPunctuation: 3,
21301     
21302     insecure: false,
21303     // private
21304     initEvents: function ()
21305     {
21306         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21307
21308         if (this.el.is('input[type=password]') && Roo.isSafari) {
21309             this.el.on('keydown', this.SafariOnKeyDown, this);
21310         }
21311
21312         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21313     },
21314     // private
21315     onRender: function (ct, position)
21316     {
21317         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21318         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21319         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21320
21321         this.trigger.createChild({
21322                    cn: [
21323                     {
21324                     //id: 'PwdMeter',
21325                     tag: 'div',
21326                     cls: 'roo-password-meter-grey col-xs-12',
21327                     style: {
21328                         //width: 0,
21329                         //width: this.meterWidth + 'px'                                                
21330                         }
21331                     },
21332                     {                            
21333                          cls: 'roo-password-meter-text'                          
21334                     }
21335                 ]            
21336         });
21337
21338          
21339         if (this.hideTrigger) {
21340             this.trigger.setDisplayed(false);
21341         }
21342         this.setSize(this.width || '', this.height || '');
21343     },
21344     // private
21345     onDestroy: function ()
21346     {
21347         if (this.trigger) {
21348             this.trigger.removeAllListeners();
21349             this.trigger.remove();
21350         }
21351         if (this.wrap) {
21352             this.wrap.remove();
21353         }
21354         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21355     },
21356     // private
21357     checkStrength: function ()
21358     {
21359         var pwd = this.inputEl().getValue();
21360         if (pwd == this._lastPwd) {
21361             return;
21362         }
21363
21364         var strength;
21365         if (this.ClientSideStrongPassword(pwd)) {
21366             strength = 3;
21367         } else if (this.ClientSideMediumPassword(pwd)) {
21368             strength = 2;
21369         } else if (this.ClientSideWeakPassword(pwd)) {
21370             strength = 1;
21371         } else {
21372             strength = 0;
21373         }
21374         
21375         Roo.log('strength1: ' + strength);
21376         
21377         //var pm = this.trigger.child('div/div/div').dom;
21378         var pm = this.trigger.child('div/div');
21379         pm.removeClass(this.meterClass);
21380         pm.addClass(this.meterClass[strength]);
21381                 
21382         
21383         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21384                 
21385         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21386         
21387         this._lastPwd = pwd;
21388     },
21389     reset: function ()
21390     {
21391         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21392         
21393         this._lastPwd = '';
21394         
21395         var pm = this.trigger.child('div/div');
21396         pm.removeClass(this.meterClass);
21397         pm.addClass('roo-password-meter-grey');        
21398         
21399         
21400         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21401         
21402         pt.innerHTML = '';
21403         this.inputEl().dom.type='password';
21404     },
21405     // private
21406     validateValue: function (value)
21407     {
21408         
21409         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21410             return false;
21411         }
21412         if (value.length == 0) {
21413             if (this.allowBlank) {
21414                 this.clearInvalid();
21415                 return true;
21416             }
21417
21418             this.markInvalid(this.errors.PwdEmpty);
21419             this.errorMsg = this.errors.PwdEmpty;
21420             return false;
21421         }
21422         
21423         if(this.insecure){
21424             return true;
21425         }
21426         
21427         if ('[\x21-\x7e]*'.match(value)) {
21428             this.markInvalid(this.errors.PwdBadChar);
21429             this.errorMsg = this.errors.PwdBadChar;
21430             return false;
21431         }
21432         if (value.length < 6) {
21433             this.markInvalid(this.errors.PwdShort);
21434             this.errorMsg = this.errors.PwdShort;
21435             return false;
21436         }
21437         if (value.length > 16) {
21438             this.markInvalid(this.errors.PwdLong);
21439             this.errorMsg = this.errors.PwdLong;
21440             return false;
21441         }
21442         var strength;
21443         if (this.ClientSideStrongPassword(value)) {
21444             strength = 3;
21445         } else if (this.ClientSideMediumPassword(value)) {
21446             strength = 2;
21447         } else if (this.ClientSideWeakPassword(value)) {
21448             strength = 1;
21449         } else {
21450             strength = 0;
21451         }
21452
21453         
21454         if (strength < 2) {
21455             //this.markInvalid(this.errors.TooWeak);
21456             this.errorMsg = this.errors.TooWeak;
21457             //return false;
21458         }
21459         
21460         
21461         console.log('strength2: ' + strength);
21462         
21463         //var pm = this.trigger.child('div/div/div').dom;
21464         
21465         var pm = this.trigger.child('div/div');
21466         pm.removeClass(this.meterClass);
21467         pm.addClass(this.meterClass[strength]);
21468                 
21469         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21470                 
21471         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21472         
21473         this.errorMsg = ''; 
21474         return true;
21475     },
21476     // private
21477     CharacterSetChecks: function (type)
21478     {
21479         this.type = type;
21480         this.fResult = false;
21481     },
21482     // private
21483     isctype: function (character, type)
21484     {
21485         switch (type) {  
21486             case this.kCapitalLetter:
21487                 if (character >= 'A' && character <= 'Z') {
21488                     return true;
21489                 }
21490                 break;
21491             
21492             case this.kSmallLetter:
21493                 if (character >= 'a' && character <= 'z') {
21494                     return true;
21495                 }
21496                 break;
21497             
21498             case this.kDigit:
21499                 if (character >= '0' && character <= '9') {
21500                     return true;
21501                 }
21502                 break;
21503             
21504             case this.kPunctuation:
21505                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21506                     return true;
21507                 }
21508                 break;
21509             
21510             default:
21511                 return false;
21512         }
21513
21514     },
21515     // private
21516     IsLongEnough: function (pwd, size)
21517     {
21518         return !(pwd == null || isNaN(size) || pwd.length < size);
21519     },
21520     // private
21521     SpansEnoughCharacterSets: function (word, nb)
21522     {
21523         if (!this.IsLongEnough(word, nb))
21524         {
21525             return false;
21526         }
21527
21528         var characterSetChecks = new Array(
21529             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21530             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21531         );
21532         
21533         for (var index = 0; index < word.length; ++index) {
21534             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21535                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21536                     characterSetChecks[nCharSet].fResult = true;
21537                     break;
21538                 }
21539             }
21540         }
21541
21542         var nCharSets = 0;
21543         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21544             if (characterSetChecks[nCharSet].fResult) {
21545                 ++nCharSets;
21546             }
21547         }
21548
21549         if (nCharSets < nb) {
21550             return false;
21551         }
21552         return true;
21553     },
21554     // private
21555     ClientSideStrongPassword: function (pwd)
21556     {
21557         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21558     },
21559     // private
21560     ClientSideMediumPassword: function (pwd)
21561     {
21562         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21563     },
21564     // private
21565     ClientSideWeakPassword: function (pwd)
21566     {
21567         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21568     }
21569           
21570 })//<script type="text/javascript">
21571
21572 /*
21573  * Based  Ext JS Library 1.1.1
21574  * Copyright(c) 2006-2007, Ext JS, LLC.
21575  * LGPL
21576  *
21577  */
21578  
21579 /**
21580  * @class Roo.HtmlEditorCore
21581  * @extends Roo.Component
21582  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21583  *
21584  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21585  */
21586
21587 Roo.HtmlEditorCore = function(config){
21588     
21589     
21590     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21591     
21592     
21593     this.addEvents({
21594         /**
21595          * @event initialize
21596          * Fires when the editor is fully initialized (including the iframe)
21597          * @param {Roo.HtmlEditorCore} this
21598          */
21599         initialize: true,
21600         /**
21601          * @event activate
21602          * Fires when the editor is first receives the focus. Any insertion must wait
21603          * until after this event.
21604          * @param {Roo.HtmlEditorCore} this
21605          */
21606         activate: true,
21607          /**
21608          * @event beforesync
21609          * Fires before the textarea is updated with content from the editor iframe. Return false
21610          * to cancel the sync.
21611          * @param {Roo.HtmlEditorCore} this
21612          * @param {String} html
21613          */
21614         beforesync: true,
21615          /**
21616          * @event beforepush
21617          * Fires before the iframe editor is updated with content from the textarea. Return false
21618          * to cancel the push.
21619          * @param {Roo.HtmlEditorCore} this
21620          * @param {String} html
21621          */
21622         beforepush: true,
21623          /**
21624          * @event sync
21625          * Fires when the textarea is updated with content from the editor iframe.
21626          * @param {Roo.HtmlEditorCore} this
21627          * @param {String} html
21628          */
21629         sync: true,
21630          /**
21631          * @event push
21632          * Fires when the iframe editor is updated with content from the textarea.
21633          * @param {Roo.HtmlEditorCore} this
21634          * @param {String} html
21635          */
21636         push: true,
21637         
21638         /**
21639          * @event editorevent
21640          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21641          * @param {Roo.HtmlEditorCore} this
21642          */
21643         editorevent: true
21644         
21645     });
21646     
21647     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21648     
21649     // defaults : white / black...
21650     this.applyBlacklists();
21651     
21652     
21653     
21654 };
21655
21656
21657 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21658
21659
21660      /**
21661      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21662      */
21663     
21664     owner : false,
21665     
21666      /**
21667      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21668      *                        Roo.resizable.
21669      */
21670     resizable : false,
21671      /**
21672      * @cfg {Number} height (in pixels)
21673      */   
21674     height: 300,
21675    /**
21676      * @cfg {Number} width (in pixels)
21677      */   
21678     width: 500,
21679     
21680     /**
21681      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21682      * 
21683      */
21684     stylesheets: false,
21685     
21686     // id of frame..
21687     frameId: false,
21688     
21689     // private properties
21690     validationEvent : false,
21691     deferHeight: true,
21692     initialized : false,
21693     activated : false,
21694     sourceEditMode : false,
21695     onFocus : Roo.emptyFn,
21696     iframePad:3,
21697     hideMode:'offsets',
21698     
21699     clearUp: true,
21700     
21701     // blacklist + whitelisted elements..
21702     black: false,
21703     white: false,
21704      
21705     bodyCls : '',
21706
21707     /**
21708      * Protected method that will not generally be called directly. It
21709      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21710      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21711      */
21712     getDocMarkup : function(){
21713         // body styles..
21714         var st = '';
21715         
21716         // inherit styels from page...?? 
21717         if (this.stylesheets === false) {
21718             
21719             Roo.get(document.head).select('style').each(function(node) {
21720                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21721             });
21722             
21723             Roo.get(document.head).select('link').each(function(node) { 
21724                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21725             });
21726             
21727         } else if (!this.stylesheets.length) {
21728                 // simple..
21729                 st = '<style type="text/css">' +
21730                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21731                    '</style>';
21732         } else { 
21733             st = '<style type="text/css">' +
21734                     this.stylesheets +
21735                 '</style>';
21736         }
21737         
21738         st +=  '<style type="text/css">' +
21739             'IMG { cursor: pointer } ' +
21740         '</style>';
21741
21742         var cls = 'roo-htmleditor-body';
21743         
21744         if(this.bodyCls.length){
21745             cls += ' ' + this.bodyCls;
21746         }
21747         
21748         return '<html><head>' + st  +
21749             //<style type="text/css">' +
21750             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21751             //'</style>' +
21752             ' </head><body class="' +  cls + '"></body></html>';
21753     },
21754
21755     // private
21756     onRender : function(ct, position)
21757     {
21758         var _t = this;
21759         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21760         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21761         
21762         
21763         this.el.dom.style.border = '0 none';
21764         this.el.dom.setAttribute('tabIndex', -1);
21765         this.el.addClass('x-hidden hide');
21766         
21767         
21768         
21769         if(Roo.isIE){ // fix IE 1px bogus margin
21770             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21771         }
21772        
21773         
21774         this.frameId = Roo.id();
21775         
21776          
21777         
21778         var iframe = this.owner.wrap.createChild({
21779             tag: 'iframe',
21780             cls: 'form-control', // bootstrap..
21781             id: this.frameId,
21782             name: this.frameId,
21783             frameBorder : 'no',
21784             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21785         }, this.el
21786         );
21787         
21788         
21789         this.iframe = iframe.dom;
21790
21791          this.assignDocWin();
21792         
21793         this.doc.designMode = 'on';
21794        
21795         this.doc.open();
21796         this.doc.write(this.getDocMarkup());
21797         this.doc.close();
21798
21799         
21800         var task = { // must defer to wait for browser to be ready
21801             run : function(){
21802                 //console.log("run task?" + this.doc.readyState);
21803                 this.assignDocWin();
21804                 if(this.doc.body || this.doc.readyState == 'complete'){
21805                     try {
21806                         this.doc.designMode="on";
21807                     } catch (e) {
21808                         return;
21809                     }
21810                     Roo.TaskMgr.stop(task);
21811                     this.initEditor.defer(10, this);
21812                 }
21813             },
21814             interval : 10,
21815             duration: 10000,
21816             scope: this
21817         };
21818         Roo.TaskMgr.start(task);
21819
21820     },
21821
21822     // private
21823     onResize : function(w, h)
21824     {
21825          Roo.log('resize: ' +w + ',' + h );
21826         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21827         if(!this.iframe){
21828             return;
21829         }
21830         if(typeof w == 'number'){
21831             
21832             this.iframe.style.width = w + 'px';
21833         }
21834         if(typeof h == 'number'){
21835             
21836             this.iframe.style.height = h + 'px';
21837             if(this.doc){
21838                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21839             }
21840         }
21841         
21842     },
21843
21844     /**
21845      * Toggles the editor between standard and source edit mode.
21846      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21847      */
21848     toggleSourceEdit : function(sourceEditMode){
21849         
21850         this.sourceEditMode = sourceEditMode === true;
21851         
21852         if(this.sourceEditMode){
21853  
21854             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21855             
21856         }else{
21857             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21858             //this.iframe.className = '';
21859             this.deferFocus();
21860         }
21861         //this.setSize(this.owner.wrap.getSize());
21862         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21863     },
21864
21865     
21866   
21867
21868     /**
21869      * Protected method that will not generally be called directly. If you need/want
21870      * custom HTML cleanup, this is the method you should override.
21871      * @param {String} html The HTML to be cleaned
21872      * return {String} The cleaned HTML
21873      */
21874     cleanHtml : function(html){
21875         html = String(html);
21876         if(html.length > 5){
21877             if(Roo.isSafari){ // strip safari nonsense
21878                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21879             }
21880         }
21881         if(html == '&nbsp;'){
21882             html = '';
21883         }
21884         return html;
21885     },
21886
21887     /**
21888      * HTML Editor -> Textarea
21889      * Protected method that will not generally be called directly. Syncs the contents
21890      * of the editor iframe with the textarea.
21891      */
21892     syncValue : function(){
21893         if(this.initialized){
21894             var bd = (this.doc.body || this.doc.documentElement);
21895             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21896             var html = bd.innerHTML;
21897             if(Roo.isSafari){
21898                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21899                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21900                 if(m && m[1]){
21901                     html = '<div style="'+m[0]+'">' + html + '</div>';
21902                 }
21903             }
21904             html = this.cleanHtml(html);
21905             // fix up the special chars.. normaly like back quotes in word...
21906             // however we do not want to do this with chinese..
21907             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21908                 var cc = b.charCodeAt();
21909                 if (
21910                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21911                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21912                     (cc >= 0xf900 && cc < 0xfb00 )
21913                 ) {
21914                         return b;
21915                 }
21916                 return "&#"+cc+";" 
21917             });
21918             if(this.owner.fireEvent('beforesync', this, html) !== false){
21919                 this.el.dom.value = html;
21920                 this.owner.fireEvent('sync', this, html);
21921             }
21922         }
21923     },
21924
21925     /**
21926      * Protected method that will not generally be called directly. Pushes the value of the textarea
21927      * into the iframe editor.
21928      */
21929     pushValue : function(){
21930         if(this.initialized){
21931             var v = this.el.dom.value.trim();
21932             
21933 //            if(v.length < 1){
21934 //                v = '&#160;';
21935 //            }
21936             
21937             if(this.owner.fireEvent('beforepush', this, v) !== false){
21938                 var d = (this.doc.body || this.doc.documentElement);
21939                 d.innerHTML = v;
21940                 this.cleanUpPaste();
21941                 this.el.dom.value = d.innerHTML;
21942                 this.owner.fireEvent('push', this, v);
21943             }
21944         }
21945     },
21946
21947     // private
21948     deferFocus : function(){
21949         this.focus.defer(10, this);
21950     },
21951
21952     // doc'ed in Field
21953     focus : function(){
21954         if(this.win && !this.sourceEditMode){
21955             this.win.focus();
21956         }else{
21957             this.el.focus();
21958         }
21959     },
21960     
21961     assignDocWin: function()
21962     {
21963         var iframe = this.iframe;
21964         
21965          if(Roo.isIE){
21966             this.doc = iframe.contentWindow.document;
21967             this.win = iframe.contentWindow;
21968         } else {
21969 //            if (!Roo.get(this.frameId)) {
21970 //                return;
21971 //            }
21972 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21973 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21974             
21975             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21976                 return;
21977             }
21978             
21979             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21980             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21981         }
21982     },
21983     
21984     // private
21985     initEditor : function(){
21986         //console.log("INIT EDITOR");
21987         this.assignDocWin();
21988         
21989         
21990         
21991         this.doc.designMode="on";
21992         this.doc.open();
21993         this.doc.write(this.getDocMarkup());
21994         this.doc.close();
21995         
21996         var dbody = (this.doc.body || this.doc.documentElement);
21997         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21998         // this copies styles from the containing element into thsi one..
21999         // not sure why we need all of this..
22000         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22001         
22002         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22003         //ss['background-attachment'] = 'fixed'; // w3c
22004         dbody.bgProperties = 'fixed'; // ie
22005         //Roo.DomHelper.applyStyles(dbody, ss);
22006         Roo.EventManager.on(this.doc, {
22007             //'mousedown': this.onEditorEvent,
22008             'mouseup': this.onEditorEvent,
22009             'dblclick': this.onEditorEvent,
22010             'click': this.onEditorEvent,
22011             'keyup': this.onEditorEvent,
22012             buffer:100,
22013             scope: this
22014         });
22015         if(Roo.isGecko){
22016             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22017         }
22018         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22019             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22020         }
22021         this.initialized = true;
22022
22023         this.owner.fireEvent('initialize', this);
22024         this.pushValue();
22025     },
22026
22027     // private
22028     onDestroy : function(){
22029         
22030         
22031         
22032         if(this.rendered){
22033             
22034             //for (var i =0; i < this.toolbars.length;i++) {
22035             //    // fixme - ask toolbars for heights?
22036             //    this.toolbars[i].onDestroy();
22037            // }
22038             
22039             //this.wrap.dom.innerHTML = '';
22040             //this.wrap.remove();
22041         }
22042     },
22043
22044     // private
22045     onFirstFocus : function(){
22046         
22047         this.assignDocWin();
22048         
22049         
22050         this.activated = true;
22051          
22052     
22053         if(Roo.isGecko){ // prevent silly gecko errors
22054             this.win.focus();
22055             var s = this.win.getSelection();
22056             if(!s.focusNode || s.focusNode.nodeType != 3){
22057                 var r = s.getRangeAt(0);
22058                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22059                 r.collapse(true);
22060                 this.deferFocus();
22061             }
22062             try{
22063                 this.execCmd('useCSS', true);
22064                 this.execCmd('styleWithCSS', false);
22065             }catch(e){}
22066         }
22067         this.owner.fireEvent('activate', this);
22068     },
22069
22070     // private
22071     adjustFont: function(btn){
22072         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22073         //if(Roo.isSafari){ // safari
22074         //    adjust *= 2;
22075        // }
22076         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22077         if(Roo.isSafari){ // safari
22078             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22079             v =  (v < 10) ? 10 : v;
22080             v =  (v > 48) ? 48 : v;
22081             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22082             
22083         }
22084         
22085         
22086         v = Math.max(1, v+adjust);
22087         
22088         this.execCmd('FontSize', v  );
22089     },
22090
22091     onEditorEvent : function(e)
22092     {
22093         this.owner.fireEvent('editorevent', this, e);
22094       //  this.updateToolbar();
22095         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22096     },
22097
22098     insertTag : function(tg)
22099     {
22100         // could be a bit smarter... -> wrap the current selected tRoo..
22101         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22102             
22103             range = this.createRange(this.getSelection());
22104             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22105             wrappingNode.appendChild(range.extractContents());
22106             range.insertNode(wrappingNode);
22107
22108             return;
22109             
22110             
22111             
22112         }
22113         this.execCmd("formatblock",   tg);
22114         
22115     },
22116     
22117     insertText : function(txt)
22118     {
22119         
22120         
22121         var range = this.createRange();
22122         range.deleteContents();
22123                //alert(Sender.getAttribute('label'));
22124                
22125         range.insertNode(this.doc.createTextNode(txt));
22126     } ,
22127     
22128      
22129
22130     /**
22131      * Executes a Midas editor command on the editor document and performs necessary focus and
22132      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22133      * @param {String} cmd The Midas command
22134      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22135      */
22136     relayCmd : function(cmd, value){
22137         this.win.focus();
22138         this.execCmd(cmd, value);
22139         this.owner.fireEvent('editorevent', this);
22140         //this.updateToolbar();
22141         this.owner.deferFocus();
22142     },
22143
22144     /**
22145      * Executes a Midas editor command directly on the editor document.
22146      * For visual commands, you should use {@link #relayCmd} instead.
22147      * <b>This should only be called after the editor is initialized.</b>
22148      * @param {String} cmd The Midas command
22149      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22150      */
22151     execCmd : function(cmd, value){
22152         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22153         this.syncValue();
22154     },
22155  
22156  
22157    
22158     /**
22159      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22160      * to insert tRoo.
22161      * @param {String} text | dom node.. 
22162      */
22163     insertAtCursor : function(text)
22164     {
22165         
22166         if(!this.activated){
22167             return;
22168         }
22169         /*
22170         if(Roo.isIE){
22171             this.win.focus();
22172             var r = this.doc.selection.createRange();
22173             if(r){
22174                 r.collapse(true);
22175                 r.pasteHTML(text);
22176                 this.syncValue();
22177                 this.deferFocus();
22178             
22179             }
22180             return;
22181         }
22182         */
22183         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22184             this.win.focus();
22185             
22186             
22187             // from jquery ui (MIT licenced)
22188             var range, node;
22189             var win = this.win;
22190             
22191             if (win.getSelection && win.getSelection().getRangeAt) {
22192                 range = win.getSelection().getRangeAt(0);
22193                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22194                 range.insertNode(node);
22195             } else if (win.document.selection && win.document.selection.createRange) {
22196                 // no firefox support
22197                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22198                 win.document.selection.createRange().pasteHTML(txt);
22199             } else {
22200                 // no firefox support
22201                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22202                 this.execCmd('InsertHTML', txt);
22203             } 
22204             
22205             this.syncValue();
22206             
22207             this.deferFocus();
22208         }
22209     },
22210  // private
22211     mozKeyPress : function(e){
22212         if(e.ctrlKey){
22213             var c = e.getCharCode(), cmd;
22214           
22215             if(c > 0){
22216                 c = String.fromCharCode(c).toLowerCase();
22217                 switch(c){
22218                     case 'b':
22219                         cmd = 'bold';
22220                         break;
22221                     case 'i':
22222                         cmd = 'italic';
22223                         break;
22224                     
22225                     case 'u':
22226                         cmd = 'underline';
22227                         break;
22228                     
22229                     case 'v':
22230                         this.cleanUpPaste.defer(100, this);
22231                         return;
22232                         
22233                 }
22234                 if(cmd){
22235                     this.win.focus();
22236                     this.execCmd(cmd);
22237                     this.deferFocus();
22238                     e.preventDefault();
22239                 }
22240                 
22241             }
22242         }
22243     },
22244
22245     // private
22246     fixKeys : function(){ // load time branching for fastest keydown performance
22247         if(Roo.isIE){
22248             return function(e){
22249                 var k = e.getKey(), r;
22250                 if(k == e.TAB){
22251                     e.stopEvent();
22252                     r = this.doc.selection.createRange();
22253                     if(r){
22254                         r.collapse(true);
22255                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22256                         this.deferFocus();
22257                     }
22258                     return;
22259                 }
22260                 
22261                 if(k == e.ENTER){
22262                     r = this.doc.selection.createRange();
22263                     if(r){
22264                         var target = r.parentElement();
22265                         if(!target || target.tagName.toLowerCase() != 'li'){
22266                             e.stopEvent();
22267                             r.pasteHTML('<br />');
22268                             r.collapse(false);
22269                             r.select();
22270                         }
22271                     }
22272                 }
22273                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22274                     this.cleanUpPaste.defer(100, this);
22275                     return;
22276                 }
22277                 
22278                 
22279             };
22280         }else if(Roo.isOpera){
22281             return function(e){
22282                 var k = e.getKey();
22283                 if(k == e.TAB){
22284                     e.stopEvent();
22285                     this.win.focus();
22286                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22287                     this.deferFocus();
22288                 }
22289                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22290                     this.cleanUpPaste.defer(100, this);
22291                     return;
22292                 }
22293                 
22294             };
22295         }else if(Roo.isSafari){
22296             return function(e){
22297                 var k = e.getKey();
22298                 
22299                 if(k == e.TAB){
22300                     e.stopEvent();
22301                     this.execCmd('InsertText','\t');
22302                     this.deferFocus();
22303                     return;
22304                 }
22305                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22306                     this.cleanUpPaste.defer(100, this);
22307                     return;
22308                 }
22309                 
22310              };
22311         }
22312     }(),
22313     
22314     getAllAncestors: function()
22315     {
22316         var p = this.getSelectedNode();
22317         var a = [];
22318         if (!p) {
22319             a.push(p); // push blank onto stack..
22320             p = this.getParentElement();
22321         }
22322         
22323         
22324         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22325             a.push(p);
22326             p = p.parentNode;
22327         }
22328         a.push(this.doc.body);
22329         return a;
22330     },
22331     lastSel : false,
22332     lastSelNode : false,
22333     
22334     
22335     getSelection : function() 
22336     {
22337         this.assignDocWin();
22338         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22339     },
22340     
22341     getSelectedNode: function() 
22342     {
22343         // this may only work on Gecko!!!
22344         
22345         // should we cache this!!!!
22346         
22347         
22348         
22349          
22350         var range = this.createRange(this.getSelection()).cloneRange();
22351         
22352         if (Roo.isIE) {
22353             var parent = range.parentElement();
22354             while (true) {
22355                 var testRange = range.duplicate();
22356                 testRange.moveToElementText(parent);
22357                 if (testRange.inRange(range)) {
22358                     break;
22359                 }
22360                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22361                     break;
22362                 }
22363                 parent = parent.parentElement;
22364             }
22365             return parent;
22366         }
22367         
22368         // is ancestor a text element.
22369         var ac =  range.commonAncestorContainer;
22370         if (ac.nodeType == 3) {
22371             ac = ac.parentNode;
22372         }
22373         
22374         var ar = ac.childNodes;
22375          
22376         var nodes = [];
22377         var other_nodes = [];
22378         var has_other_nodes = false;
22379         for (var i=0;i<ar.length;i++) {
22380             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22381                 continue;
22382             }
22383             // fullly contained node.
22384             
22385             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22386                 nodes.push(ar[i]);
22387                 continue;
22388             }
22389             
22390             // probably selected..
22391             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22392                 other_nodes.push(ar[i]);
22393                 continue;
22394             }
22395             // outer..
22396             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22397                 continue;
22398             }
22399             
22400             
22401             has_other_nodes = true;
22402         }
22403         if (!nodes.length && other_nodes.length) {
22404             nodes= other_nodes;
22405         }
22406         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22407             return false;
22408         }
22409         
22410         return nodes[0];
22411     },
22412     createRange: function(sel)
22413     {
22414         // this has strange effects when using with 
22415         // top toolbar - not sure if it's a great idea.
22416         //this.editor.contentWindow.focus();
22417         if (typeof sel != "undefined") {
22418             try {
22419                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22420             } catch(e) {
22421                 return this.doc.createRange();
22422             }
22423         } else {
22424             return this.doc.createRange();
22425         }
22426     },
22427     getParentElement: function()
22428     {
22429         
22430         this.assignDocWin();
22431         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22432         
22433         var range = this.createRange(sel);
22434          
22435         try {
22436             var p = range.commonAncestorContainer;
22437             while (p.nodeType == 3) { // text node
22438                 p = p.parentNode;
22439             }
22440             return p;
22441         } catch (e) {
22442             return null;
22443         }
22444     
22445     },
22446     /***
22447      *
22448      * Range intersection.. the hard stuff...
22449      *  '-1' = before
22450      *  '0' = hits..
22451      *  '1' = after.
22452      *         [ -- selected range --- ]
22453      *   [fail]                        [fail]
22454      *
22455      *    basically..
22456      *      if end is before start or  hits it. fail.
22457      *      if start is after end or hits it fail.
22458      *
22459      *   if either hits (but other is outside. - then it's not 
22460      *   
22461      *    
22462      **/
22463     
22464     
22465     // @see http://www.thismuchiknow.co.uk/?p=64.
22466     rangeIntersectsNode : function(range, node)
22467     {
22468         var nodeRange = node.ownerDocument.createRange();
22469         try {
22470             nodeRange.selectNode(node);
22471         } catch (e) {
22472             nodeRange.selectNodeContents(node);
22473         }
22474     
22475         var rangeStartRange = range.cloneRange();
22476         rangeStartRange.collapse(true);
22477     
22478         var rangeEndRange = range.cloneRange();
22479         rangeEndRange.collapse(false);
22480     
22481         var nodeStartRange = nodeRange.cloneRange();
22482         nodeStartRange.collapse(true);
22483     
22484         var nodeEndRange = nodeRange.cloneRange();
22485         nodeEndRange.collapse(false);
22486     
22487         return rangeStartRange.compareBoundaryPoints(
22488                  Range.START_TO_START, nodeEndRange) == -1 &&
22489                rangeEndRange.compareBoundaryPoints(
22490                  Range.START_TO_START, nodeStartRange) == 1;
22491         
22492          
22493     },
22494     rangeCompareNode : function(range, node)
22495     {
22496         var nodeRange = node.ownerDocument.createRange();
22497         try {
22498             nodeRange.selectNode(node);
22499         } catch (e) {
22500             nodeRange.selectNodeContents(node);
22501         }
22502         
22503         
22504         range.collapse(true);
22505     
22506         nodeRange.collapse(true);
22507      
22508         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22509         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22510          
22511         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22512         
22513         var nodeIsBefore   =  ss == 1;
22514         var nodeIsAfter    = ee == -1;
22515         
22516         if (nodeIsBefore && nodeIsAfter) {
22517             return 0; // outer
22518         }
22519         if (!nodeIsBefore && nodeIsAfter) {
22520             return 1; //right trailed.
22521         }
22522         
22523         if (nodeIsBefore && !nodeIsAfter) {
22524             return 2;  // left trailed.
22525         }
22526         // fully contined.
22527         return 3;
22528     },
22529
22530     // private? - in a new class?
22531     cleanUpPaste :  function()
22532     {
22533         // cleans up the whole document..
22534         Roo.log('cleanuppaste');
22535         
22536         this.cleanUpChildren(this.doc.body);
22537         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22538         if (clean != this.doc.body.innerHTML) {
22539             this.doc.body.innerHTML = clean;
22540         }
22541         
22542     },
22543     
22544     cleanWordChars : function(input) {// change the chars to hex code
22545         var he = Roo.HtmlEditorCore;
22546         
22547         var output = input;
22548         Roo.each(he.swapCodes, function(sw) { 
22549             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22550             
22551             output = output.replace(swapper, sw[1]);
22552         });
22553         
22554         return output;
22555     },
22556     
22557     
22558     cleanUpChildren : function (n)
22559     {
22560         if (!n.childNodes.length) {
22561             return;
22562         }
22563         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22564            this.cleanUpChild(n.childNodes[i]);
22565         }
22566     },
22567     
22568     
22569         
22570     
22571     cleanUpChild : function (node)
22572     {
22573         var ed = this;
22574         //console.log(node);
22575         if (node.nodeName == "#text") {
22576             // clean up silly Windows -- stuff?
22577             return; 
22578         }
22579         if (node.nodeName == "#comment") {
22580             node.parentNode.removeChild(node);
22581             // clean up silly Windows -- stuff?
22582             return; 
22583         }
22584         var lcname = node.tagName.toLowerCase();
22585         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22586         // whitelist of tags..
22587         
22588         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22589             // remove node.
22590             node.parentNode.removeChild(node);
22591             return;
22592             
22593         }
22594         
22595         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22596         
22597         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22598         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22599         
22600         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22601         //    remove_keep_children = true;
22602         //}
22603         
22604         if (remove_keep_children) {
22605             this.cleanUpChildren(node);
22606             // inserts everything just before this node...
22607             while (node.childNodes.length) {
22608                 var cn = node.childNodes[0];
22609                 node.removeChild(cn);
22610                 node.parentNode.insertBefore(cn, node);
22611             }
22612             node.parentNode.removeChild(node);
22613             return;
22614         }
22615         
22616         if (!node.attributes || !node.attributes.length) {
22617             this.cleanUpChildren(node);
22618             return;
22619         }
22620         
22621         function cleanAttr(n,v)
22622         {
22623             
22624             if (v.match(/^\./) || v.match(/^\//)) {
22625                 return;
22626             }
22627             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22628                 return;
22629             }
22630             if (v.match(/^#/)) {
22631                 return;
22632             }
22633 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22634             node.removeAttribute(n);
22635             
22636         }
22637         
22638         var cwhite = this.cwhite;
22639         var cblack = this.cblack;
22640             
22641         function cleanStyle(n,v)
22642         {
22643             if (v.match(/expression/)) { //XSS?? should we even bother..
22644                 node.removeAttribute(n);
22645                 return;
22646             }
22647             
22648             var parts = v.split(/;/);
22649             var clean = [];
22650             
22651             Roo.each(parts, function(p) {
22652                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22653                 if (!p.length) {
22654                     return true;
22655                 }
22656                 var l = p.split(':').shift().replace(/\s+/g,'');
22657                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22658                 
22659                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22660 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22661                     //node.removeAttribute(n);
22662                     return true;
22663                 }
22664                 //Roo.log()
22665                 // only allow 'c whitelisted system attributes'
22666                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22667 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22668                     //node.removeAttribute(n);
22669                     return true;
22670                 }
22671                 
22672                 
22673                  
22674                 
22675                 clean.push(p);
22676                 return true;
22677             });
22678             if (clean.length) { 
22679                 node.setAttribute(n, clean.join(';'));
22680             } else {
22681                 node.removeAttribute(n);
22682             }
22683             
22684         }
22685         
22686         
22687         for (var i = node.attributes.length-1; i > -1 ; i--) {
22688             var a = node.attributes[i];
22689             //console.log(a);
22690             
22691             if (a.name.toLowerCase().substr(0,2)=='on')  {
22692                 node.removeAttribute(a.name);
22693                 continue;
22694             }
22695             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22696                 node.removeAttribute(a.name);
22697                 continue;
22698             }
22699             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22700                 cleanAttr(a.name,a.value); // fixme..
22701                 continue;
22702             }
22703             if (a.name == 'style') {
22704                 cleanStyle(a.name,a.value);
22705                 continue;
22706             }
22707             /// clean up MS crap..
22708             // tecnically this should be a list of valid class'es..
22709             
22710             
22711             if (a.name == 'class') {
22712                 if (a.value.match(/^Mso/)) {
22713                     node.className = '';
22714                 }
22715                 
22716                 if (a.value.match(/^body$/)) {
22717                     node.className = '';
22718                 }
22719                 continue;
22720             }
22721             
22722             // style cleanup!?
22723             // class cleanup?
22724             
22725         }
22726         
22727         
22728         this.cleanUpChildren(node);
22729         
22730         
22731     },
22732     
22733     /**
22734      * Clean up MS wordisms...
22735      */
22736     cleanWord : function(node)
22737     {
22738         
22739         
22740         if (!node) {
22741             this.cleanWord(this.doc.body);
22742             return;
22743         }
22744         if (node.nodeName == "#text") {
22745             // clean up silly Windows -- stuff?
22746             return; 
22747         }
22748         if (node.nodeName == "#comment") {
22749             node.parentNode.removeChild(node);
22750             // clean up silly Windows -- stuff?
22751             return; 
22752         }
22753         
22754         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22755             node.parentNode.removeChild(node);
22756             return;
22757         }
22758         
22759         // remove - but keep children..
22760         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22761             while (node.childNodes.length) {
22762                 var cn = node.childNodes[0];
22763                 node.removeChild(cn);
22764                 node.parentNode.insertBefore(cn, node);
22765             }
22766             node.parentNode.removeChild(node);
22767             this.iterateChildren(node, this.cleanWord);
22768             return;
22769         }
22770         // clean styles
22771         if (node.className.length) {
22772             
22773             var cn = node.className.split(/\W+/);
22774             var cna = [];
22775             Roo.each(cn, function(cls) {
22776                 if (cls.match(/Mso[a-zA-Z]+/)) {
22777                     return;
22778                 }
22779                 cna.push(cls);
22780             });
22781             node.className = cna.length ? cna.join(' ') : '';
22782             if (!cna.length) {
22783                 node.removeAttribute("class");
22784             }
22785         }
22786         
22787         if (node.hasAttribute("lang")) {
22788             node.removeAttribute("lang");
22789         }
22790         
22791         if (node.hasAttribute("style")) {
22792             
22793             var styles = node.getAttribute("style").split(";");
22794             var nstyle = [];
22795             Roo.each(styles, function(s) {
22796                 if (!s.match(/:/)) {
22797                     return;
22798                 }
22799                 var kv = s.split(":");
22800                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22801                     return;
22802                 }
22803                 // what ever is left... we allow.
22804                 nstyle.push(s);
22805             });
22806             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22807             if (!nstyle.length) {
22808                 node.removeAttribute('style');
22809             }
22810         }
22811         this.iterateChildren(node, this.cleanWord);
22812         
22813         
22814         
22815     },
22816     /**
22817      * iterateChildren of a Node, calling fn each time, using this as the scole..
22818      * @param {DomNode} node node to iterate children of.
22819      * @param {Function} fn method of this class to call on each item.
22820      */
22821     iterateChildren : function(node, fn)
22822     {
22823         if (!node.childNodes.length) {
22824                 return;
22825         }
22826         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22827            fn.call(this, node.childNodes[i])
22828         }
22829     },
22830     
22831     
22832     /**
22833      * cleanTableWidths.
22834      *
22835      * Quite often pasting from word etc.. results in tables with column and widths.
22836      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22837      *
22838      */
22839     cleanTableWidths : function(node)
22840     {
22841          
22842          
22843         if (!node) {
22844             this.cleanTableWidths(this.doc.body);
22845             return;
22846         }
22847         
22848         // ignore list...
22849         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22850             return; 
22851         }
22852         Roo.log(node.tagName);
22853         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22854             this.iterateChildren(node, this.cleanTableWidths);
22855             return;
22856         }
22857         if (node.hasAttribute('width')) {
22858             node.removeAttribute('width');
22859         }
22860         
22861          
22862         if (node.hasAttribute("style")) {
22863             // pretty basic...
22864             
22865             var styles = node.getAttribute("style").split(";");
22866             var nstyle = [];
22867             Roo.each(styles, function(s) {
22868                 if (!s.match(/:/)) {
22869                     return;
22870                 }
22871                 var kv = s.split(":");
22872                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22873                     return;
22874                 }
22875                 // what ever is left... we allow.
22876                 nstyle.push(s);
22877             });
22878             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22879             if (!nstyle.length) {
22880                 node.removeAttribute('style');
22881             }
22882         }
22883         
22884         this.iterateChildren(node, this.cleanTableWidths);
22885         
22886         
22887     },
22888     
22889     
22890     
22891     
22892     domToHTML : function(currentElement, depth, nopadtext) {
22893         
22894         depth = depth || 0;
22895         nopadtext = nopadtext || false;
22896     
22897         if (!currentElement) {
22898             return this.domToHTML(this.doc.body);
22899         }
22900         
22901         //Roo.log(currentElement);
22902         var j;
22903         var allText = false;
22904         var nodeName = currentElement.nodeName;
22905         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22906         
22907         if  (nodeName == '#text') {
22908             
22909             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22910         }
22911         
22912         
22913         var ret = '';
22914         if (nodeName != 'BODY') {
22915              
22916             var i = 0;
22917             // Prints the node tagName, such as <A>, <IMG>, etc
22918             if (tagName) {
22919                 var attr = [];
22920                 for(i = 0; i < currentElement.attributes.length;i++) {
22921                     // quoting?
22922                     var aname = currentElement.attributes.item(i).name;
22923                     if (!currentElement.attributes.item(i).value.length) {
22924                         continue;
22925                     }
22926                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22927                 }
22928                 
22929                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22930             } 
22931             else {
22932                 
22933                 // eack
22934             }
22935         } else {
22936             tagName = false;
22937         }
22938         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22939             return ret;
22940         }
22941         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22942             nopadtext = true;
22943         }
22944         
22945         
22946         // Traverse the tree
22947         i = 0;
22948         var currentElementChild = currentElement.childNodes.item(i);
22949         var allText = true;
22950         var innerHTML  = '';
22951         lastnode = '';
22952         while (currentElementChild) {
22953             // Formatting code (indent the tree so it looks nice on the screen)
22954             var nopad = nopadtext;
22955             if (lastnode == 'SPAN') {
22956                 nopad  = true;
22957             }
22958             // text
22959             if  (currentElementChild.nodeName == '#text') {
22960                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22961                 toadd = nopadtext ? toadd : toadd.trim();
22962                 if (!nopad && toadd.length > 80) {
22963                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22964                 }
22965                 innerHTML  += toadd;
22966                 
22967                 i++;
22968                 currentElementChild = currentElement.childNodes.item(i);
22969                 lastNode = '';
22970                 continue;
22971             }
22972             allText = false;
22973             
22974             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22975                 
22976             // Recursively traverse the tree structure of the child node
22977             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22978             lastnode = currentElementChild.nodeName;
22979             i++;
22980             currentElementChild=currentElement.childNodes.item(i);
22981         }
22982         
22983         ret += innerHTML;
22984         
22985         if (!allText) {
22986                 // The remaining code is mostly for formatting the tree
22987             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22988         }
22989         
22990         
22991         if (tagName) {
22992             ret+= "</"+tagName+">";
22993         }
22994         return ret;
22995         
22996     },
22997         
22998     applyBlacklists : function()
22999     {
23000         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23001         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23002         
23003         this.white = [];
23004         this.black = [];
23005         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23006             if (b.indexOf(tag) > -1) {
23007                 return;
23008             }
23009             this.white.push(tag);
23010             
23011         }, this);
23012         
23013         Roo.each(w, function(tag) {
23014             if (b.indexOf(tag) > -1) {
23015                 return;
23016             }
23017             if (this.white.indexOf(tag) > -1) {
23018                 return;
23019             }
23020             this.white.push(tag);
23021             
23022         }, this);
23023         
23024         
23025         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23026             if (w.indexOf(tag) > -1) {
23027                 return;
23028             }
23029             this.black.push(tag);
23030             
23031         }, this);
23032         
23033         Roo.each(b, function(tag) {
23034             if (w.indexOf(tag) > -1) {
23035                 return;
23036             }
23037             if (this.black.indexOf(tag) > -1) {
23038                 return;
23039             }
23040             this.black.push(tag);
23041             
23042         }, this);
23043         
23044         
23045         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23046         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23047         
23048         this.cwhite = [];
23049         this.cblack = [];
23050         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23051             if (b.indexOf(tag) > -1) {
23052                 return;
23053             }
23054             this.cwhite.push(tag);
23055             
23056         }, this);
23057         
23058         Roo.each(w, function(tag) {
23059             if (b.indexOf(tag) > -1) {
23060                 return;
23061             }
23062             if (this.cwhite.indexOf(tag) > -1) {
23063                 return;
23064             }
23065             this.cwhite.push(tag);
23066             
23067         }, this);
23068         
23069         
23070         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23071             if (w.indexOf(tag) > -1) {
23072                 return;
23073             }
23074             this.cblack.push(tag);
23075             
23076         }, this);
23077         
23078         Roo.each(b, function(tag) {
23079             if (w.indexOf(tag) > -1) {
23080                 return;
23081             }
23082             if (this.cblack.indexOf(tag) > -1) {
23083                 return;
23084             }
23085             this.cblack.push(tag);
23086             
23087         }, this);
23088     },
23089     
23090     setStylesheets : function(stylesheets)
23091     {
23092         if(typeof(stylesheets) == 'string'){
23093             Roo.get(this.iframe.contentDocument.head).createChild({
23094                 tag : 'link',
23095                 rel : 'stylesheet',
23096                 type : 'text/css',
23097                 href : stylesheets
23098             });
23099             
23100             return;
23101         }
23102         var _this = this;
23103      
23104         Roo.each(stylesheets, function(s) {
23105             if(!s.length){
23106                 return;
23107             }
23108             
23109             Roo.get(_this.iframe.contentDocument.head).createChild({
23110                 tag : 'link',
23111                 rel : 'stylesheet',
23112                 type : 'text/css',
23113                 href : s
23114             });
23115         });
23116
23117         
23118     },
23119     
23120     removeStylesheets : function()
23121     {
23122         var _this = this;
23123         
23124         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23125             s.remove();
23126         });
23127     },
23128     
23129     setStyle : function(style)
23130     {
23131         Roo.get(this.iframe.contentDocument.head).createChild({
23132             tag : 'style',
23133             type : 'text/css',
23134             html : style
23135         });
23136
23137         return;
23138     }
23139     
23140     // hide stuff that is not compatible
23141     /**
23142      * @event blur
23143      * @hide
23144      */
23145     /**
23146      * @event change
23147      * @hide
23148      */
23149     /**
23150      * @event focus
23151      * @hide
23152      */
23153     /**
23154      * @event specialkey
23155      * @hide
23156      */
23157     /**
23158      * @cfg {String} fieldClass @hide
23159      */
23160     /**
23161      * @cfg {String} focusClass @hide
23162      */
23163     /**
23164      * @cfg {String} autoCreate @hide
23165      */
23166     /**
23167      * @cfg {String} inputType @hide
23168      */
23169     /**
23170      * @cfg {String} invalidClass @hide
23171      */
23172     /**
23173      * @cfg {String} invalidText @hide
23174      */
23175     /**
23176      * @cfg {String} msgFx @hide
23177      */
23178     /**
23179      * @cfg {String} validateOnBlur @hide
23180      */
23181 });
23182
23183 Roo.HtmlEditorCore.white = [
23184         'area', 'br', 'img', 'input', 'hr', 'wbr',
23185         
23186        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23187        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23188        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23189        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23190        'table',   'ul',         'xmp', 
23191        
23192        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23193       'thead',   'tr', 
23194      
23195       'dir', 'menu', 'ol', 'ul', 'dl',
23196        
23197       'embed',  'object'
23198 ];
23199
23200
23201 Roo.HtmlEditorCore.black = [
23202     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23203         'applet', // 
23204         'base',   'basefont', 'bgsound', 'blink',  'body', 
23205         'frame',  'frameset', 'head',    'html',   'ilayer', 
23206         'iframe', 'layer',  'link',     'meta',    'object',   
23207         'script', 'style' ,'title',  'xml' // clean later..
23208 ];
23209 Roo.HtmlEditorCore.clean = [
23210     'script', 'style', 'title', 'xml'
23211 ];
23212 Roo.HtmlEditorCore.remove = [
23213     'font'
23214 ];
23215 // attributes..
23216
23217 Roo.HtmlEditorCore.ablack = [
23218     'on'
23219 ];
23220     
23221 Roo.HtmlEditorCore.aclean = [ 
23222     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23223 ];
23224
23225 // protocols..
23226 Roo.HtmlEditorCore.pwhite= [
23227         'http',  'https',  'mailto'
23228 ];
23229
23230 // white listed style attributes.
23231 Roo.HtmlEditorCore.cwhite= [
23232       //  'text-align', /// default is to allow most things..
23233       
23234          
23235 //        'font-size'//??
23236 ];
23237
23238 // black listed style attributes.
23239 Roo.HtmlEditorCore.cblack= [
23240       //  'font-size' -- this can be set by the project 
23241 ];
23242
23243
23244 Roo.HtmlEditorCore.swapCodes   =[ 
23245     [    8211, "--" ], 
23246     [    8212, "--" ], 
23247     [    8216,  "'" ],  
23248     [    8217, "'" ],  
23249     [    8220, '"' ],  
23250     [    8221, '"' ],  
23251     [    8226, "*" ],  
23252     [    8230, "..." ]
23253 ]; 
23254
23255     /*
23256  * - LGPL
23257  *
23258  * HtmlEditor
23259  * 
23260  */
23261
23262 /**
23263  * @class Roo.bootstrap.HtmlEditor
23264  * @extends Roo.bootstrap.TextArea
23265  * Bootstrap HtmlEditor class
23266
23267  * @constructor
23268  * Create a new HtmlEditor
23269  * @param {Object} config The config object
23270  */
23271
23272 Roo.bootstrap.HtmlEditor = function(config){
23273     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23274     if (!this.toolbars) {
23275         this.toolbars = [];
23276     }
23277     
23278     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23279     this.addEvents({
23280             /**
23281              * @event initialize
23282              * Fires when the editor is fully initialized (including the iframe)
23283              * @param {HtmlEditor} this
23284              */
23285             initialize: true,
23286             /**
23287              * @event activate
23288              * Fires when the editor is first receives the focus. Any insertion must wait
23289              * until after this event.
23290              * @param {HtmlEditor} this
23291              */
23292             activate: true,
23293              /**
23294              * @event beforesync
23295              * Fires before the textarea is updated with content from the editor iframe. Return false
23296              * to cancel the sync.
23297              * @param {HtmlEditor} this
23298              * @param {String} html
23299              */
23300             beforesync: true,
23301              /**
23302              * @event beforepush
23303              * Fires before the iframe editor is updated with content from the textarea. Return false
23304              * to cancel the push.
23305              * @param {HtmlEditor} this
23306              * @param {String} html
23307              */
23308             beforepush: true,
23309              /**
23310              * @event sync
23311              * Fires when the textarea is updated with content from the editor iframe.
23312              * @param {HtmlEditor} this
23313              * @param {String} html
23314              */
23315             sync: true,
23316              /**
23317              * @event push
23318              * Fires when the iframe editor is updated with content from the textarea.
23319              * @param {HtmlEditor} this
23320              * @param {String} html
23321              */
23322             push: true,
23323              /**
23324              * @event editmodechange
23325              * Fires when the editor switches edit modes
23326              * @param {HtmlEditor} this
23327              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23328              */
23329             editmodechange: true,
23330             /**
23331              * @event editorevent
23332              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23333              * @param {HtmlEditor} this
23334              */
23335             editorevent: true,
23336             /**
23337              * @event firstfocus
23338              * Fires when on first focus - needed by toolbars..
23339              * @param {HtmlEditor} this
23340              */
23341             firstfocus: true,
23342             /**
23343              * @event autosave
23344              * Auto save the htmlEditor value as a file into Events
23345              * @param {HtmlEditor} this
23346              */
23347             autosave: true,
23348             /**
23349              * @event savedpreview
23350              * preview the saved version of htmlEditor
23351              * @param {HtmlEditor} this
23352              */
23353             savedpreview: true
23354         });
23355 };
23356
23357
23358 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23359     
23360     
23361       /**
23362      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23363      */
23364     toolbars : false,
23365     
23366      /**
23367     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23368     */
23369     btns : [],
23370    
23371      /**
23372      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23373      *                        Roo.resizable.
23374      */
23375     resizable : false,
23376      /**
23377      * @cfg {Number} height (in pixels)
23378      */   
23379     height: 300,
23380    /**
23381      * @cfg {Number} width (in pixels)
23382      */   
23383     width: false,
23384     
23385     /**
23386      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23387      * 
23388      */
23389     stylesheets: false,
23390     
23391     // id of frame..
23392     frameId: false,
23393     
23394     // private properties
23395     validationEvent : false,
23396     deferHeight: true,
23397     initialized : false,
23398     activated : false,
23399     
23400     onFocus : Roo.emptyFn,
23401     iframePad:3,
23402     hideMode:'offsets',
23403     
23404     tbContainer : false,
23405     
23406     bodyCls : '',
23407     
23408     toolbarContainer :function() {
23409         return this.wrap.select('.x-html-editor-tb',true).first();
23410     },
23411
23412     /**
23413      * Protected method that will not generally be called directly. It
23414      * is called when the editor creates its toolbar. Override this method if you need to
23415      * add custom toolbar buttons.
23416      * @param {HtmlEditor} editor
23417      */
23418     createToolbar : function(){
23419         Roo.log('renewing');
23420         Roo.log("create toolbars");
23421         
23422         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23423         this.toolbars[0].render(this.toolbarContainer());
23424         
23425         return;
23426         
23427 //        if (!editor.toolbars || !editor.toolbars.length) {
23428 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23429 //        }
23430 //        
23431 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23432 //            editor.toolbars[i] = Roo.factory(
23433 //                    typeof(editor.toolbars[i]) == 'string' ?
23434 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23435 //                Roo.bootstrap.HtmlEditor);
23436 //            editor.toolbars[i].init(editor);
23437 //        }
23438     },
23439
23440      
23441     // private
23442     onRender : function(ct, position)
23443     {
23444        // Roo.log("Call onRender: " + this.xtype);
23445         var _t = this;
23446         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23447       
23448         this.wrap = this.inputEl().wrap({
23449             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23450         });
23451         
23452         this.editorcore.onRender(ct, position);
23453          
23454         if (this.resizable) {
23455             this.resizeEl = new Roo.Resizable(this.wrap, {
23456                 pinned : true,
23457                 wrap: true,
23458                 dynamic : true,
23459                 minHeight : this.height,
23460                 height: this.height,
23461                 handles : this.resizable,
23462                 width: this.width,
23463                 listeners : {
23464                     resize : function(r, w, h) {
23465                         _t.onResize(w,h); // -something
23466                     }
23467                 }
23468             });
23469             
23470         }
23471         this.createToolbar(this);
23472        
23473         
23474         if(!this.width && this.resizable){
23475             this.setSize(this.wrap.getSize());
23476         }
23477         if (this.resizeEl) {
23478             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23479             // should trigger onReize..
23480         }
23481         
23482     },
23483
23484     // private
23485     onResize : function(w, h)
23486     {
23487         Roo.log('resize: ' +w + ',' + h );
23488         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23489         var ew = false;
23490         var eh = false;
23491         
23492         if(this.inputEl() ){
23493             if(typeof w == 'number'){
23494                 var aw = w - this.wrap.getFrameWidth('lr');
23495                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23496                 ew = aw;
23497             }
23498             if(typeof h == 'number'){
23499                  var tbh = -11;  // fixme it needs to tool bar size!
23500                 for (var i =0; i < this.toolbars.length;i++) {
23501                     // fixme - ask toolbars for heights?
23502                     tbh += this.toolbars[i].el.getHeight();
23503                     //if (this.toolbars[i].footer) {
23504                     //    tbh += this.toolbars[i].footer.el.getHeight();
23505                     //}
23506                 }
23507               
23508                 
23509                 
23510                 
23511                 
23512                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23513                 ah -= 5; // knock a few pixes off for look..
23514                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23515                 var eh = ah;
23516             }
23517         }
23518         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23519         this.editorcore.onResize(ew,eh);
23520         
23521     },
23522
23523     /**
23524      * Toggles the editor between standard and source edit mode.
23525      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23526      */
23527     toggleSourceEdit : function(sourceEditMode)
23528     {
23529         this.editorcore.toggleSourceEdit(sourceEditMode);
23530         
23531         if(this.editorcore.sourceEditMode){
23532             Roo.log('editor - showing textarea');
23533             
23534 //            Roo.log('in');
23535 //            Roo.log(this.syncValue());
23536             this.syncValue();
23537             this.inputEl().removeClass(['hide', 'x-hidden']);
23538             this.inputEl().dom.removeAttribute('tabIndex');
23539             this.inputEl().focus();
23540         }else{
23541             Roo.log('editor - hiding textarea');
23542 //            Roo.log('out')
23543 //            Roo.log(this.pushValue()); 
23544             this.pushValue();
23545             
23546             this.inputEl().addClass(['hide', 'x-hidden']);
23547             this.inputEl().dom.setAttribute('tabIndex', -1);
23548             //this.deferFocus();
23549         }
23550          
23551         if(this.resizable){
23552             this.setSize(this.wrap.getSize());
23553         }
23554         
23555         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23556     },
23557  
23558     // private (for BoxComponent)
23559     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23560
23561     // private (for BoxComponent)
23562     getResizeEl : function(){
23563         return this.wrap;
23564     },
23565
23566     // private (for BoxComponent)
23567     getPositionEl : function(){
23568         return this.wrap;
23569     },
23570
23571     // private
23572     initEvents : function(){
23573         this.originalValue = this.getValue();
23574     },
23575
23576 //    /**
23577 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23578 //     * @method
23579 //     */
23580 //    markInvalid : Roo.emptyFn,
23581 //    /**
23582 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23583 //     * @method
23584 //     */
23585 //    clearInvalid : Roo.emptyFn,
23586
23587     setValue : function(v){
23588         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23589         this.editorcore.pushValue();
23590     },
23591
23592      
23593     // private
23594     deferFocus : function(){
23595         this.focus.defer(10, this);
23596     },
23597
23598     // doc'ed in Field
23599     focus : function(){
23600         this.editorcore.focus();
23601         
23602     },
23603       
23604
23605     // private
23606     onDestroy : function(){
23607         
23608         
23609         
23610         if(this.rendered){
23611             
23612             for (var i =0; i < this.toolbars.length;i++) {
23613                 // fixme - ask toolbars for heights?
23614                 this.toolbars[i].onDestroy();
23615             }
23616             
23617             this.wrap.dom.innerHTML = '';
23618             this.wrap.remove();
23619         }
23620     },
23621
23622     // private
23623     onFirstFocus : function(){
23624         //Roo.log("onFirstFocus");
23625         this.editorcore.onFirstFocus();
23626          for (var i =0; i < this.toolbars.length;i++) {
23627             this.toolbars[i].onFirstFocus();
23628         }
23629         
23630     },
23631     
23632     // private
23633     syncValue : function()
23634     {   
23635         this.editorcore.syncValue();
23636     },
23637     
23638     pushValue : function()
23639     {   
23640         this.editorcore.pushValue();
23641     }
23642      
23643     
23644     // hide stuff that is not compatible
23645     /**
23646      * @event blur
23647      * @hide
23648      */
23649     /**
23650      * @event change
23651      * @hide
23652      */
23653     /**
23654      * @event focus
23655      * @hide
23656      */
23657     /**
23658      * @event specialkey
23659      * @hide
23660      */
23661     /**
23662      * @cfg {String} fieldClass @hide
23663      */
23664     /**
23665      * @cfg {String} focusClass @hide
23666      */
23667     /**
23668      * @cfg {String} autoCreate @hide
23669      */
23670     /**
23671      * @cfg {String} inputType @hide
23672      */
23673     /**
23674      * @cfg {String} invalidClass @hide
23675      */
23676     /**
23677      * @cfg {String} invalidText @hide
23678      */
23679     /**
23680      * @cfg {String} msgFx @hide
23681      */
23682     /**
23683      * @cfg {String} validateOnBlur @hide
23684      */
23685 });
23686  
23687     
23688    
23689    
23690    
23691       
23692 Roo.namespace('Roo.bootstrap.htmleditor');
23693 /**
23694  * @class Roo.bootstrap.HtmlEditorToolbar1
23695  * Basic Toolbar
23696  * 
23697  * Usage:
23698  *
23699  new Roo.bootstrap.HtmlEditor({
23700     ....
23701     toolbars : [
23702         new Roo.bootstrap.HtmlEditorToolbar1({
23703             disable : { fonts: 1 , format: 1, ..., ... , ...],
23704             btns : [ .... ]
23705         })
23706     }
23707      
23708  * 
23709  * @cfg {Object} disable List of elements to disable..
23710  * @cfg {Array} btns List of additional buttons.
23711  * 
23712  * 
23713  * NEEDS Extra CSS? 
23714  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23715  */
23716  
23717 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23718 {
23719     
23720     Roo.apply(this, config);
23721     
23722     // default disabled, based on 'good practice'..
23723     this.disable = this.disable || {};
23724     Roo.applyIf(this.disable, {
23725         fontSize : true,
23726         colors : true,
23727         specialElements : true
23728     });
23729     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23730     
23731     this.editor = config.editor;
23732     this.editorcore = config.editor.editorcore;
23733     
23734     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23735     
23736     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23737     // dont call parent... till later.
23738 }
23739 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23740      
23741     bar : true,
23742     
23743     editor : false,
23744     editorcore : false,
23745     
23746     
23747     formats : [
23748         "p" ,  
23749         "h1","h2","h3","h4","h5","h6", 
23750         "pre", "code", 
23751         "abbr", "acronym", "address", "cite", "samp", "var",
23752         'div','span'
23753     ],
23754     
23755     onRender : function(ct, position)
23756     {
23757        // Roo.log("Call onRender: " + this.xtype);
23758         
23759        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23760        Roo.log(this.el);
23761        this.el.dom.style.marginBottom = '0';
23762        var _this = this;
23763        var editorcore = this.editorcore;
23764        var editor= this.editor;
23765        
23766        var children = [];
23767        var btn = function(id,cmd , toggle, handler, html){
23768        
23769             var  event = toggle ? 'toggle' : 'click';
23770        
23771             var a = {
23772                 size : 'sm',
23773                 xtype: 'Button',
23774                 xns: Roo.bootstrap,
23775                 glyphicon : id,
23776                 cmd : id || cmd,
23777                 enableToggle:toggle !== false,
23778                 html : html || '',
23779                 pressed : toggle ? false : null,
23780                 listeners : {}
23781             };
23782             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23783                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23784             };
23785             children.push(a);
23786             return a;
23787        }
23788        
23789     //    var cb_box = function...
23790         
23791         var style = {
23792                 xtype: 'Button',
23793                 size : 'sm',
23794                 xns: Roo.bootstrap,
23795                 glyphicon : 'font',
23796                 //html : 'submit'
23797                 menu : {
23798                     xtype: 'Menu',
23799                     xns: Roo.bootstrap,
23800                     items:  []
23801                 }
23802         };
23803         Roo.each(this.formats, function(f) {
23804             style.menu.items.push({
23805                 xtype :'MenuItem',
23806                 xns: Roo.bootstrap,
23807                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23808                 tagname : f,
23809                 listeners : {
23810                     click : function()
23811                     {
23812                         editorcore.insertTag(this.tagname);
23813                         editor.focus();
23814                     }
23815                 }
23816                 
23817             });
23818         });
23819         children.push(style);   
23820         
23821         btn('bold',false,true);
23822         btn('italic',false,true);
23823         btn('align-left', 'justifyleft',true);
23824         btn('align-center', 'justifycenter',true);
23825         btn('align-right' , 'justifyright',true);
23826         btn('link', false, false, function(btn) {
23827             //Roo.log("create link?");
23828             var url = prompt(this.createLinkText, this.defaultLinkValue);
23829             if(url && url != 'http:/'+'/'){
23830                 this.editorcore.relayCmd('createlink', url);
23831             }
23832         }),
23833         btn('list','insertunorderedlist',true);
23834         btn('pencil', false,true, function(btn){
23835                 Roo.log(this);
23836                 this.toggleSourceEdit(btn.pressed);
23837         });
23838         
23839         if (this.editor.btns.length > 0) {
23840             for (var i = 0; i<this.editor.btns.length; i++) {
23841                 children.push(this.editor.btns[i]);
23842             }
23843         }
23844         
23845         /*
23846         var cog = {
23847                 xtype: 'Button',
23848                 size : 'sm',
23849                 xns: Roo.bootstrap,
23850                 glyphicon : 'cog',
23851                 //html : 'submit'
23852                 menu : {
23853                     xtype: 'Menu',
23854                     xns: Roo.bootstrap,
23855                     items:  []
23856                 }
23857         };
23858         
23859         cog.menu.items.push({
23860             xtype :'MenuItem',
23861             xns: Roo.bootstrap,
23862             html : Clean styles,
23863             tagname : f,
23864             listeners : {
23865                 click : function()
23866                 {
23867                     editorcore.insertTag(this.tagname);
23868                     editor.focus();
23869                 }
23870             }
23871             
23872         });
23873        */
23874         
23875          
23876        this.xtype = 'NavSimplebar';
23877         
23878         for(var i=0;i< children.length;i++) {
23879             
23880             this.buttons.add(this.addxtypeChild(children[i]));
23881             
23882         }
23883         
23884         editor.on('editorevent', this.updateToolbar, this);
23885     },
23886     onBtnClick : function(id)
23887     {
23888        this.editorcore.relayCmd(id);
23889        this.editorcore.focus();
23890     },
23891     
23892     /**
23893      * Protected method that will not generally be called directly. It triggers
23894      * a toolbar update by reading the markup state of the current selection in the editor.
23895      */
23896     updateToolbar: function(){
23897
23898         if(!this.editorcore.activated){
23899             this.editor.onFirstFocus(); // is this neeed?
23900             return;
23901         }
23902
23903         var btns = this.buttons; 
23904         var doc = this.editorcore.doc;
23905         btns.get('bold').setActive(doc.queryCommandState('bold'));
23906         btns.get('italic').setActive(doc.queryCommandState('italic'));
23907         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23908         
23909         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23910         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23911         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23912         
23913         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23914         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23915          /*
23916         
23917         var ans = this.editorcore.getAllAncestors();
23918         if (this.formatCombo) {
23919             
23920             
23921             var store = this.formatCombo.store;
23922             this.formatCombo.setValue("");
23923             for (var i =0; i < ans.length;i++) {
23924                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23925                     // select it..
23926                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23927                     break;
23928                 }
23929             }
23930         }
23931         
23932         
23933         
23934         // hides menus... - so this cant be on a menu...
23935         Roo.bootstrap.MenuMgr.hideAll();
23936         */
23937         Roo.bootstrap.MenuMgr.hideAll();
23938         //this.editorsyncValue();
23939     },
23940     onFirstFocus: function() {
23941         this.buttons.each(function(item){
23942            item.enable();
23943         });
23944     },
23945     toggleSourceEdit : function(sourceEditMode){
23946         
23947           
23948         if(sourceEditMode){
23949             Roo.log("disabling buttons");
23950            this.buttons.each( function(item){
23951                 if(item.cmd != 'pencil'){
23952                     item.disable();
23953                 }
23954             });
23955           
23956         }else{
23957             Roo.log("enabling buttons");
23958             if(this.editorcore.initialized){
23959                 this.buttons.each( function(item){
23960                     item.enable();
23961                 });
23962             }
23963             
23964         }
23965         Roo.log("calling toggole on editor");
23966         // tell the editor that it's been pressed..
23967         this.editor.toggleSourceEdit(sourceEditMode);
23968        
23969     }
23970 });
23971
23972
23973
23974
23975
23976 /**
23977  * @class Roo.bootstrap.Table.AbstractSelectionModel
23978  * @extends Roo.util.Observable
23979  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23980  * implemented by descendant classes.  This class should not be directly instantiated.
23981  * @constructor
23982  */
23983 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23984     this.locked = false;
23985     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23986 };
23987
23988
23989 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23990     /** @ignore Called by the grid automatically. Do not call directly. */
23991     init : function(grid){
23992         this.grid = grid;
23993         this.initEvents();
23994     },
23995
23996     /**
23997      * Locks the selections.
23998      */
23999     lock : function(){
24000         this.locked = true;
24001     },
24002
24003     /**
24004      * Unlocks the selections.
24005      */
24006     unlock : function(){
24007         this.locked = false;
24008     },
24009
24010     /**
24011      * Returns true if the selections are locked.
24012      * @return {Boolean}
24013      */
24014     isLocked : function(){
24015         return this.locked;
24016     }
24017 });
24018 /**
24019  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24020  * @class Roo.bootstrap.Table.RowSelectionModel
24021  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24022  * It supports multiple selections and keyboard selection/navigation. 
24023  * @constructor
24024  * @param {Object} config
24025  */
24026
24027 Roo.bootstrap.Table.RowSelectionModel = function(config){
24028     Roo.apply(this, config);
24029     this.selections = new Roo.util.MixedCollection(false, function(o){
24030         return o.id;
24031     });
24032
24033     this.last = false;
24034     this.lastActive = false;
24035
24036     this.addEvents({
24037         /**
24038              * @event selectionchange
24039              * Fires when the selection changes
24040              * @param {SelectionModel} this
24041              */
24042             "selectionchange" : true,
24043         /**
24044              * @event afterselectionchange
24045              * Fires after the selection changes (eg. by key press or clicking)
24046              * @param {SelectionModel} this
24047              */
24048             "afterselectionchange" : true,
24049         /**
24050              * @event beforerowselect
24051              * Fires when a row is selected being selected, return false to cancel.
24052              * @param {SelectionModel} this
24053              * @param {Number} rowIndex The selected index
24054              * @param {Boolean} keepExisting False if other selections will be cleared
24055              */
24056             "beforerowselect" : true,
24057         /**
24058              * @event rowselect
24059              * Fires when a row is selected.
24060              * @param {SelectionModel} this
24061              * @param {Number} rowIndex The selected index
24062              * @param {Roo.data.Record} r The record
24063              */
24064             "rowselect" : true,
24065         /**
24066              * @event rowdeselect
24067              * Fires when a row is deselected.
24068              * @param {SelectionModel} this
24069              * @param {Number} rowIndex The selected index
24070              */
24071         "rowdeselect" : true
24072     });
24073     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24074     this.locked = false;
24075  };
24076
24077 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24078     /**
24079      * @cfg {Boolean} singleSelect
24080      * True to allow selection of only one row at a time (defaults to false)
24081      */
24082     singleSelect : false,
24083
24084     // private
24085     initEvents : function()
24086     {
24087
24088         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24089         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24090         //}else{ // allow click to work like normal
24091          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24092         //}
24093         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24094         this.grid.on("rowclick", this.handleMouseDown, this);
24095         
24096         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24097             "up" : function(e){
24098                 if(!e.shiftKey){
24099                     this.selectPrevious(e.shiftKey);
24100                 }else if(this.last !== false && this.lastActive !== false){
24101                     var last = this.last;
24102                     this.selectRange(this.last,  this.lastActive-1);
24103                     this.grid.getView().focusRow(this.lastActive);
24104                     if(last !== false){
24105                         this.last = last;
24106                     }
24107                 }else{
24108                     this.selectFirstRow();
24109                 }
24110                 this.fireEvent("afterselectionchange", this);
24111             },
24112             "down" : function(e){
24113                 if(!e.shiftKey){
24114                     this.selectNext(e.shiftKey);
24115                 }else if(this.last !== false && this.lastActive !== false){
24116                     var last = this.last;
24117                     this.selectRange(this.last,  this.lastActive+1);
24118                     this.grid.getView().focusRow(this.lastActive);
24119                     if(last !== false){
24120                         this.last = last;
24121                     }
24122                 }else{
24123                     this.selectFirstRow();
24124                 }
24125                 this.fireEvent("afterselectionchange", this);
24126             },
24127             scope: this
24128         });
24129         this.grid.store.on('load', function(){
24130             this.selections.clear();
24131         },this);
24132         /*
24133         var view = this.grid.view;
24134         view.on("refresh", this.onRefresh, this);
24135         view.on("rowupdated", this.onRowUpdated, this);
24136         view.on("rowremoved", this.onRemove, this);
24137         */
24138     },
24139
24140     // private
24141     onRefresh : function()
24142     {
24143         var ds = this.grid.store, i, v = this.grid.view;
24144         var s = this.selections;
24145         s.each(function(r){
24146             if((i = ds.indexOfId(r.id)) != -1){
24147                 v.onRowSelect(i);
24148             }else{
24149                 s.remove(r);
24150             }
24151         });
24152     },
24153
24154     // private
24155     onRemove : function(v, index, r){
24156         this.selections.remove(r);
24157     },
24158
24159     // private
24160     onRowUpdated : function(v, index, r){
24161         if(this.isSelected(r)){
24162             v.onRowSelect(index);
24163         }
24164     },
24165
24166     /**
24167      * Select records.
24168      * @param {Array} records The records to select
24169      * @param {Boolean} keepExisting (optional) True to keep existing selections
24170      */
24171     selectRecords : function(records, keepExisting)
24172     {
24173         if(!keepExisting){
24174             this.clearSelections();
24175         }
24176             var ds = this.grid.store;
24177         for(var i = 0, len = records.length; i < len; i++){
24178             this.selectRow(ds.indexOf(records[i]), true);
24179         }
24180     },
24181
24182     /**
24183      * Gets the number of selected rows.
24184      * @return {Number}
24185      */
24186     getCount : function(){
24187         return this.selections.length;
24188     },
24189
24190     /**
24191      * Selects the first row in the grid.
24192      */
24193     selectFirstRow : function(){
24194         this.selectRow(0);
24195     },
24196
24197     /**
24198      * Select the last row.
24199      * @param {Boolean} keepExisting (optional) True to keep existing selections
24200      */
24201     selectLastRow : function(keepExisting){
24202         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24203         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24204     },
24205
24206     /**
24207      * Selects the row immediately following the last selected row.
24208      * @param {Boolean} keepExisting (optional) True to keep existing selections
24209      */
24210     selectNext : function(keepExisting)
24211     {
24212             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24213             this.selectRow(this.last+1, keepExisting);
24214             this.grid.getView().focusRow(this.last);
24215         }
24216     },
24217
24218     /**
24219      * Selects the row that precedes the last selected row.
24220      * @param {Boolean} keepExisting (optional) True to keep existing selections
24221      */
24222     selectPrevious : function(keepExisting){
24223         if(this.last){
24224             this.selectRow(this.last-1, keepExisting);
24225             this.grid.getView().focusRow(this.last);
24226         }
24227     },
24228
24229     /**
24230      * Returns the selected records
24231      * @return {Array} Array of selected records
24232      */
24233     getSelections : function(){
24234         return [].concat(this.selections.items);
24235     },
24236
24237     /**
24238      * Returns the first selected record.
24239      * @return {Record}
24240      */
24241     getSelected : function(){
24242         return this.selections.itemAt(0);
24243     },
24244
24245
24246     /**
24247      * Clears all selections.
24248      */
24249     clearSelections : function(fast)
24250     {
24251         if(this.locked) {
24252             return;
24253         }
24254         if(fast !== true){
24255                 var ds = this.grid.store;
24256             var s = this.selections;
24257             s.each(function(r){
24258                 this.deselectRow(ds.indexOfId(r.id));
24259             }, this);
24260             s.clear();
24261         }else{
24262             this.selections.clear();
24263         }
24264         this.last = false;
24265     },
24266
24267
24268     /**
24269      * Selects all rows.
24270      */
24271     selectAll : function(){
24272         if(this.locked) {
24273             return;
24274         }
24275         this.selections.clear();
24276         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24277             this.selectRow(i, true);
24278         }
24279     },
24280
24281     /**
24282      * Returns True if there is a selection.
24283      * @return {Boolean}
24284      */
24285     hasSelection : function(){
24286         return this.selections.length > 0;
24287     },
24288
24289     /**
24290      * Returns True if the specified row is selected.
24291      * @param {Number/Record} record The record or index of the record to check
24292      * @return {Boolean}
24293      */
24294     isSelected : function(index){
24295             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24296         return (r && this.selections.key(r.id) ? true : false);
24297     },
24298
24299     /**
24300      * Returns True if the specified record id is selected.
24301      * @param {String} id The id of record to check
24302      * @return {Boolean}
24303      */
24304     isIdSelected : function(id){
24305         return (this.selections.key(id) ? true : false);
24306     },
24307
24308
24309     // private
24310     handleMouseDBClick : function(e, t){
24311         
24312     },
24313     // private
24314     handleMouseDown : function(e, t)
24315     {
24316             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24317         if(this.isLocked() || rowIndex < 0 ){
24318             return;
24319         };
24320         if(e.shiftKey && this.last !== false){
24321             var last = this.last;
24322             this.selectRange(last, rowIndex, e.ctrlKey);
24323             this.last = last; // reset the last
24324             t.focus();
24325     
24326         }else{
24327             var isSelected = this.isSelected(rowIndex);
24328             //Roo.log("select row:" + rowIndex);
24329             if(isSelected){
24330                 this.deselectRow(rowIndex);
24331             } else {
24332                         this.selectRow(rowIndex, true);
24333             }
24334     
24335             /*
24336                 if(e.button !== 0 && isSelected){
24337                 alert('rowIndex 2: ' + rowIndex);
24338                     view.focusRow(rowIndex);
24339                 }else if(e.ctrlKey && isSelected){
24340                     this.deselectRow(rowIndex);
24341                 }else if(!isSelected){
24342                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24343                     view.focusRow(rowIndex);
24344                 }
24345             */
24346         }
24347         this.fireEvent("afterselectionchange", this);
24348     },
24349     // private
24350     handleDragableRowClick :  function(grid, rowIndex, e) 
24351     {
24352         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24353             this.selectRow(rowIndex, false);
24354             grid.view.focusRow(rowIndex);
24355              this.fireEvent("afterselectionchange", this);
24356         }
24357     },
24358     
24359     /**
24360      * Selects multiple rows.
24361      * @param {Array} rows Array of the indexes of the row to select
24362      * @param {Boolean} keepExisting (optional) True to keep existing selections
24363      */
24364     selectRows : function(rows, keepExisting){
24365         if(!keepExisting){
24366             this.clearSelections();
24367         }
24368         for(var i = 0, len = rows.length; i < len; i++){
24369             this.selectRow(rows[i], true);
24370         }
24371     },
24372
24373     /**
24374      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24375      * @param {Number} startRow The index of the first row in the range
24376      * @param {Number} endRow The index of the last row in the range
24377      * @param {Boolean} keepExisting (optional) True to retain existing selections
24378      */
24379     selectRange : function(startRow, endRow, keepExisting){
24380         if(this.locked) {
24381             return;
24382         }
24383         if(!keepExisting){
24384             this.clearSelections();
24385         }
24386         if(startRow <= endRow){
24387             for(var i = startRow; i <= endRow; i++){
24388                 this.selectRow(i, true);
24389             }
24390         }else{
24391             for(var i = startRow; i >= endRow; i--){
24392                 this.selectRow(i, true);
24393             }
24394         }
24395     },
24396
24397     /**
24398      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24399      * @param {Number} startRow The index of the first row in the range
24400      * @param {Number} endRow The index of the last row in the range
24401      */
24402     deselectRange : function(startRow, endRow, preventViewNotify){
24403         if(this.locked) {
24404             return;
24405         }
24406         for(var i = startRow; i <= endRow; i++){
24407             this.deselectRow(i, preventViewNotify);
24408         }
24409     },
24410
24411     /**
24412      * Selects a row.
24413      * @param {Number} row The index of the row to select
24414      * @param {Boolean} keepExisting (optional) True to keep existing selections
24415      */
24416     selectRow : function(index, keepExisting, preventViewNotify)
24417     {
24418             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24419             return;
24420         }
24421         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24422             if(!keepExisting || this.singleSelect){
24423                 this.clearSelections();
24424             }
24425             
24426             var r = this.grid.store.getAt(index);
24427             //console.log('selectRow - record id :' + r.id);
24428             
24429             this.selections.add(r);
24430             this.last = this.lastActive = index;
24431             if(!preventViewNotify){
24432                 var proxy = new Roo.Element(
24433                                 this.grid.getRowDom(index)
24434                 );
24435                 proxy.addClass('bg-info info');
24436             }
24437             this.fireEvent("rowselect", this, index, r);
24438             this.fireEvent("selectionchange", this);
24439         }
24440     },
24441
24442     /**
24443      * Deselects a row.
24444      * @param {Number} row The index of the row to deselect
24445      */
24446     deselectRow : function(index, preventViewNotify)
24447     {
24448         if(this.locked) {
24449             return;
24450         }
24451         if(this.last == index){
24452             this.last = false;
24453         }
24454         if(this.lastActive == index){
24455             this.lastActive = false;
24456         }
24457         
24458         var r = this.grid.store.getAt(index);
24459         if (!r) {
24460             return;
24461         }
24462         
24463         this.selections.remove(r);
24464         //.console.log('deselectRow - record id :' + r.id);
24465         if(!preventViewNotify){
24466         
24467             var proxy = new Roo.Element(
24468                 this.grid.getRowDom(index)
24469             );
24470             proxy.removeClass('bg-info info');
24471         }
24472         this.fireEvent("rowdeselect", this, index);
24473         this.fireEvent("selectionchange", this);
24474     },
24475
24476     // private
24477     restoreLast : function(){
24478         if(this._last){
24479             this.last = this._last;
24480         }
24481     },
24482
24483     // private
24484     acceptsNav : function(row, col, cm){
24485         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24486     },
24487
24488     // private
24489     onEditorKey : function(field, e){
24490         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24491         if(k == e.TAB){
24492             e.stopEvent();
24493             ed.completeEdit();
24494             if(e.shiftKey){
24495                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24496             }else{
24497                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24498             }
24499         }else if(k == e.ENTER && !e.ctrlKey){
24500             e.stopEvent();
24501             ed.completeEdit();
24502             if(e.shiftKey){
24503                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24504             }else{
24505                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24506             }
24507         }else if(k == e.ESC){
24508             ed.cancelEdit();
24509         }
24510         if(newCell){
24511             g.startEditing(newCell[0], newCell[1]);
24512         }
24513     }
24514 });
24515 /*
24516  * Based on:
24517  * Ext JS Library 1.1.1
24518  * Copyright(c) 2006-2007, Ext JS, LLC.
24519  *
24520  * Originally Released Under LGPL - original licence link has changed is not relivant.
24521  *
24522  * Fork - LGPL
24523  * <script type="text/javascript">
24524  */
24525  
24526 /**
24527  * @class Roo.bootstrap.PagingToolbar
24528  * @extends Roo.bootstrap.NavSimplebar
24529  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24530  * @constructor
24531  * Create a new PagingToolbar
24532  * @param {Object} config The config object
24533  * @param {Roo.data.Store} store
24534  */
24535 Roo.bootstrap.PagingToolbar = function(config)
24536 {
24537     // old args format still supported... - xtype is prefered..
24538         // created from xtype...
24539     
24540     this.ds = config.dataSource;
24541     
24542     if (config.store && !this.ds) {
24543         this.store= Roo.factory(config.store, Roo.data);
24544         this.ds = this.store;
24545         this.ds.xmodule = this.xmodule || false;
24546     }
24547     
24548     this.toolbarItems = [];
24549     if (config.items) {
24550         this.toolbarItems = config.items;
24551     }
24552     
24553     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24554     
24555     this.cursor = 0;
24556     
24557     if (this.ds) { 
24558         this.bind(this.ds);
24559     }
24560     
24561     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24562     
24563 };
24564
24565 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24566     /**
24567      * @cfg {Roo.data.Store} dataSource
24568      * The underlying data store providing the paged data
24569      */
24570     /**
24571      * @cfg {String/HTMLElement/Element} container
24572      * container The id or element that will contain the toolbar
24573      */
24574     /**
24575      * @cfg {Boolean} displayInfo
24576      * True to display the displayMsg (defaults to false)
24577      */
24578     /**
24579      * @cfg {Number} pageSize
24580      * The number of records to display per page (defaults to 20)
24581      */
24582     pageSize: 20,
24583     /**
24584      * @cfg {String} displayMsg
24585      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24586      */
24587     displayMsg : 'Displaying {0} - {1} of {2}',
24588     /**
24589      * @cfg {String} emptyMsg
24590      * The message to display when no records are found (defaults to "No data to display")
24591      */
24592     emptyMsg : 'No data to display',
24593     /**
24594      * Customizable piece of the default paging text (defaults to "Page")
24595      * @type String
24596      */
24597     beforePageText : "Page",
24598     /**
24599      * Customizable piece of the default paging text (defaults to "of %0")
24600      * @type String
24601      */
24602     afterPageText : "of {0}",
24603     /**
24604      * Customizable piece of the default paging text (defaults to "First Page")
24605      * @type String
24606      */
24607     firstText : "First Page",
24608     /**
24609      * Customizable piece of the default paging text (defaults to "Previous Page")
24610      * @type String
24611      */
24612     prevText : "Previous Page",
24613     /**
24614      * Customizable piece of the default paging text (defaults to "Next Page")
24615      * @type String
24616      */
24617     nextText : "Next Page",
24618     /**
24619      * Customizable piece of the default paging text (defaults to "Last Page")
24620      * @type String
24621      */
24622     lastText : "Last Page",
24623     /**
24624      * Customizable piece of the default paging text (defaults to "Refresh")
24625      * @type String
24626      */
24627     refreshText : "Refresh",
24628
24629     buttons : false,
24630     // private
24631     onRender : function(ct, position) 
24632     {
24633         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24634         this.navgroup.parentId = this.id;
24635         this.navgroup.onRender(this.el, null);
24636         // add the buttons to the navgroup
24637         
24638         if(this.displayInfo){
24639             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24640             this.displayEl = this.el.select('.x-paging-info', true).first();
24641 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24642 //            this.displayEl = navel.el.select('span',true).first();
24643         }
24644         
24645         var _this = this;
24646         
24647         if(this.buttons){
24648             Roo.each(_this.buttons, function(e){ // this might need to use render????
24649                Roo.factory(e).render(_this.el);
24650             });
24651         }
24652             
24653         Roo.each(_this.toolbarItems, function(e) {
24654             _this.navgroup.addItem(e);
24655         });
24656         
24657         
24658         this.first = this.navgroup.addItem({
24659             tooltip: this.firstText,
24660             cls: "prev",
24661             icon : 'fa fa-backward',
24662             disabled: true,
24663             preventDefault: true,
24664             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24665         });
24666         
24667         this.prev =  this.navgroup.addItem({
24668             tooltip: this.prevText,
24669             cls: "prev",
24670             icon : 'fa fa-step-backward',
24671             disabled: true,
24672             preventDefault: true,
24673             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24674         });
24675     //this.addSeparator();
24676         
24677         
24678         var field = this.navgroup.addItem( {
24679             tagtype : 'span',
24680             cls : 'x-paging-position',
24681             
24682             html : this.beforePageText  +
24683                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24684                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24685          } ); //?? escaped?
24686         
24687         this.field = field.el.select('input', true).first();
24688         this.field.on("keydown", this.onPagingKeydown, this);
24689         this.field.on("focus", function(){this.dom.select();});
24690     
24691     
24692         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24693         //this.field.setHeight(18);
24694         //this.addSeparator();
24695         this.next = this.navgroup.addItem({
24696             tooltip: this.nextText,
24697             cls: "next",
24698             html : ' <i class="fa fa-step-forward">',
24699             disabled: true,
24700             preventDefault: true,
24701             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24702         });
24703         this.last = this.navgroup.addItem({
24704             tooltip: this.lastText,
24705             icon : 'fa fa-forward',
24706             cls: "next",
24707             disabled: true,
24708             preventDefault: true,
24709             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24710         });
24711     //this.addSeparator();
24712         this.loading = this.navgroup.addItem({
24713             tooltip: this.refreshText,
24714             icon: 'fa fa-refresh',
24715             preventDefault: true,
24716             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24717         });
24718         
24719     },
24720
24721     // private
24722     updateInfo : function(){
24723         if(this.displayEl){
24724             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24725             var msg = count == 0 ?
24726                 this.emptyMsg :
24727                 String.format(
24728                     this.displayMsg,
24729                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24730                 );
24731             this.displayEl.update(msg);
24732         }
24733     },
24734
24735     // private
24736     onLoad : function(ds, r, o)
24737     {
24738         this.cursor = o.params.start ? o.params.start : 0;
24739         
24740         var d = this.getPageData(),
24741             ap = d.activePage,
24742             ps = d.pages;
24743         
24744         
24745         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24746         this.field.dom.value = ap;
24747         this.first.setDisabled(ap == 1);
24748         this.prev.setDisabled(ap == 1);
24749         this.next.setDisabled(ap == ps);
24750         this.last.setDisabled(ap == ps);
24751         this.loading.enable();
24752         this.updateInfo();
24753     },
24754
24755     // private
24756     getPageData : function(){
24757         var total = this.ds.getTotalCount();
24758         return {
24759             total : total,
24760             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24761             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24762         };
24763     },
24764
24765     // private
24766     onLoadError : function(){
24767         this.loading.enable();
24768     },
24769
24770     // private
24771     onPagingKeydown : function(e){
24772         var k = e.getKey();
24773         var d = this.getPageData();
24774         if(k == e.RETURN){
24775             var v = this.field.dom.value, pageNum;
24776             if(!v || isNaN(pageNum = parseInt(v, 10))){
24777                 this.field.dom.value = d.activePage;
24778                 return;
24779             }
24780             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24781             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24782             e.stopEvent();
24783         }
24784         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))
24785         {
24786           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24787           this.field.dom.value = pageNum;
24788           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24789           e.stopEvent();
24790         }
24791         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24792         {
24793           var v = this.field.dom.value, pageNum; 
24794           var increment = (e.shiftKey) ? 10 : 1;
24795           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24796                 increment *= -1;
24797           }
24798           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24799             this.field.dom.value = d.activePage;
24800             return;
24801           }
24802           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24803           {
24804             this.field.dom.value = parseInt(v, 10) + increment;
24805             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24806             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24807           }
24808           e.stopEvent();
24809         }
24810     },
24811
24812     // private
24813     beforeLoad : function(){
24814         if(this.loading){
24815             this.loading.disable();
24816         }
24817     },
24818
24819     // private
24820     onClick : function(which){
24821         
24822         var ds = this.ds;
24823         if (!ds) {
24824             return;
24825         }
24826         
24827         switch(which){
24828             case "first":
24829                 ds.load({params:{start: 0, limit: this.pageSize}});
24830             break;
24831             case "prev":
24832                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24833             break;
24834             case "next":
24835                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24836             break;
24837             case "last":
24838                 var total = ds.getTotalCount();
24839                 var extra = total % this.pageSize;
24840                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24841                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24842             break;
24843             case "refresh":
24844                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24845             break;
24846         }
24847     },
24848
24849     /**
24850      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24851      * @param {Roo.data.Store} store The data store to unbind
24852      */
24853     unbind : function(ds){
24854         ds.un("beforeload", this.beforeLoad, this);
24855         ds.un("load", this.onLoad, this);
24856         ds.un("loadexception", this.onLoadError, this);
24857         ds.un("remove", this.updateInfo, this);
24858         ds.un("add", this.updateInfo, this);
24859         this.ds = undefined;
24860     },
24861
24862     /**
24863      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24864      * @param {Roo.data.Store} store The data store to bind
24865      */
24866     bind : function(ds){
24867         ds.on("beforeload", this.beforeLoad, this);
24868         ds.on("load", this.onLoad, this);
24869         ds.on("loadexception", this.onLoadError, this);
24870         ds.on("remove", this.updateInfo, this);
24871         ds.on("add", this.updateInfo, this);
24872         this.ds = ds;
24873     }
24874 });/*
24875  * - LGPL
24876  *
24877  * element
24878  * 
24879  */
24880
24881 /**
24882  * @class Roo.bootstrap.MessageBar
24883  * @extends Roo.bootstrap.Component
24884  * Bootstrap MessageBar class
24885  * @cfg {String} html contents of the MessageBar
24886  * @cfg {String} weight (info | success | warning | danger) default info
24887  * @cfg {String} beforeClass insert the bar before the given class
24888  * @cfg {Boolean} closable (true | false) default false
24889  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24890  * 
24891  * @constructor
24892  * Create a new Element
24893  * @param {Object} config The config object
24894  */
24895
24896 Roo.bootstrap.MessageBar = function(config){
24897     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24898 };
24899
24900 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24901     
24902     html: '',
24903     weight: 'info',
24904     closable: false,
24905     fixed: false,
24906     beforeClass: 'bootstrap-sticky-wrap',
24907     
24908     getAutoCreate : function(){
24909         
24910         var cfg = {
24911             tag: 'div',
24912             cls: 'alert alert-dismissable alert-' + this.weight,
24913             cn: [
24914                 {
24915                     tag: 'span',
24916                     cls: 'message',
24917                     html: this.html || ''
24918                 }
24919             ]
24920         };
24921         
24922         if(this.fixed){
24923             cfg.cls += ' alert-messages-fixed';
24924         }
24925         
24926         if(this.closable){
24927             cfg.cn.push({
24928                 tag: 'button',
24929                 cls: 'close',
24930                 html: 'x'
24931             });
24932         }
24933         
24934         return cfg;
24935     },
24936     
24937     onRender : function(ct, position)
24938     {
24939         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24940         
24941         if(!this.el){
24942             var cfg = Roo.apply({},  this.getAutoCreate());
24943             cfg.id = Roo.id();
24944             
24945             if (this.cls) {
24946                 cfg.cls += ' ' + this.cls;
24947             }
24948             if (this.style) {
24949                 cfg.style = this.style;
24950             }
24951             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24952             
24953             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24954         }
24955         
24956         this.el.select('>button.close').on('click', this.hide, this);
24957         
24958     },
24959     
24960     show : function()
24961     {
24962         if (!this.rendered) {
24963             this.render();
24964         }
24965         
24966         this.el.show();
24967         
24968         this.fireEvent('show', this);
24969         
24970     },
24971     
24972     hide : function()
24973     {
24974         if (!this.rendered) {
24975             this.render();
24976         }
24977         
24978         this.el.hide();
24979         
24980         this.fireEvent('hide', this);
24981     },
24982     
24983     update : function()
24984     {
24985 //        var e = this.el.dom.firstChild;
24986 //        
24987 //        if(this.closable){
24988 //            e = e.nextSibling;
24989 //        }
24990 //        
24991 //        e.data = this.html || '';
24992
24993         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24994     }
24995    
24996 });
24997
24998  
24999
25000      /*
25001  * - LGPL
25002  *
25003  * Graph
25004  * 
25005  */
25006
25007
25008 /**
25009  * @class Roo.bootstrap.Graph
25010  * @extends Roo.bootstrap.Component
25011  * Bootstrap Graph class
25012 > Prameters
25013  -sm {number} sm 4
25014  -md {number} md 5
25015  @cfg {String} graphtype  bar | vbar | pie
25016  @cfg {number} g_x coodinator | centre x (pie)
25017  @cfg {number} g_y coodinator | centre y (pie)
25018  @cfg {number} g_r radius (pie)
25019  @cfg {number} g_height height of the chart (respected by all elements in the set)
25020  @cfg {number} g_width width of the chart (respected by all elements in the set)
25021  @cfg {Object} title The title of the chart
25022     
25023  -{Array}  values
25024  -opts (object) options for the chart 
25025      o {
25026      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25027      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25028      o vgutter (number)
25029      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.
25030      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25031      o to
25032      o stretch (boolean)
25033      o }
25034  -opts (object) options for the pie
25035      o{
25036      o cut
25037      o startAngle (number)
25038      o endAngle (number)
25039      } 
25040  *
25041  * @constructor
25042  * Create a new Input
25043  * @param {Object} config The config object
25044  */
25045
25046 Roo.bootstrap.Graph = function(config){
25047     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25048     
25049     this.addEvents({
25050         // img events
25051         /**
25052          * @event click
25053          * The img click event for the img.
25054          * @param {Roo.EventObject} e
25055          */
25056         "click" : true
25057     });
25058 };
25059
25060 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25061     
25062     sm: 4,
25063     md: 5,
25064     graphtype: 'bar',
25065     g_height: 250,
25066     g_width: 400,
25067     g_x: 50,
25068     g_y: 50,
25069     g_r: 30,
25070     opts:{
25071         //g_colors: this.colors,
25072         g_type: 'soft',
25073         g_gutter: '20%'
25074
25075     },
25076     title : false,
25077
25078     getAutoCreate : function(){
25079         
25080         var cfg = {
25081             tag: 'div',
25082             html : null
25083         };
25084         
25085         
25086         return  cfg;
25087     },
25088
25089     onRender : function(ct,position){
25090         
25091         
25092         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25093         
25094         if (typeof(Raphael) == 'undefined') {
25095             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25096             return;
25097         }
25098         
25099         this.raphael = Raphael(this.el.dom);
25100         
25101                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25102                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25103                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25104                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25105                 /*
25106                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25107                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25108                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25109                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25110                 
25111                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25112                 r.barchart(330, 10, 300, 220, data1);
25113                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25114                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25115                 */
25116                 
25117                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25118                 // r.barchart(30, 30, 560, 250,  xdata, {
25119                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25120                 //     axis : "0 0 1 1",
25121                 //     axisxlabels :  xdata
25122                 //     //yvalues : cols,
25123                    
25124                 // });
25125 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25126 //        
25127 //        this.load(null,xdata,{
25128 //                axis : "0 0 1 1",
25129 //                axisxlabels :  xdata
25130 //                });
25131
25132     },
25133
25134     load : function(graphtype,xdata,opts)
25135     {
25136         this.raphael.clear();
25137         if(!graphtype) {
25138             graphtype = this.graphtype;
25139         }
25140         if(!opts){
25141             opts = this.opts;
25142         }
25143         var r = this.raphael,
25144             fin = function () {
25145                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25146             },
25147             fout = function () {
25148                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25149             },
25150             pfin = function() {
25151                 this.sector.stop();
25152                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25153
25154                 if (this.label) {
25155                     this.label[0].stop();
25156                     this.label[0].attr({ r: 7.5 });
25157                     this.label[1].attr({ "font-weight": 800 });
25158                 }
25159             },
25160             pfout = function() {
25161                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25162
25163                 if (this.label) {
25164                     this.label[0].animate({ r: 5 }, 500, "bounce");
25165                     this.label[1].attr({ "font-weight": 400 });
25166                 }
25167             };
25168
25169         switch(graphtype){
25170             case 'bar':
25171                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25172                 break;
25173             case 'hbar':
25174                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25175                 break;
25176             case 'pie':
25177 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25178 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25179 //            
25180                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25181                 
25182                 break;
25183
25184         }
25185         
25186         if(this.title){
25187             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25188         }
25189         
25190     },
25191     
25192     setTitle: function(o)
25193     {
25194         this.title = o;
25195     },
25196     
25197     initEvents: function() {
25198         
25199         if(!this.href){
25200             this.el.on('click', this.onClick, this);
25201         }
25202     },
25203     
25204     onClick : function(e)
25205     {
25206         Roo.log('img onclick');
25207         this.fireEvent('click', this, e);
25208     }
25209    
25210 });
25211
25212  
25213 /*
25214  * - LGPL
25215  *
25216  * numberBox
25217  * 
25218  */
25219 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25220
25221 /**
25222  * @class Roo.bootstrap.dash.NumberBox
25223  * @extends Roo.bootstrap.Component
25224  * Bootstrap NumberBox class
25225  * @cfg {String} headline Box headline
25226  * @cfg {String} content Box content
25227  * @cfg {String} icon Box icon
25228  * @cfg {String} footer Footer text
25229  * @cfg {String} fhref Footer href
25230  * 
25231  * @constructor
25232  * Create a new NumberBox
25233  * @param {Object} config The config object
25234  */
25235
25236
25237 Roo.bootstrap.dash.NumberBox = function(config){
25238     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25239     
25240 };
25241
25242 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25243     
25244     headline : '',
25245     content : '',
25246     icon : '',
25247     footer : '',
25248     fhref : '',
25249     ficon : '',
25250     
25251     getAutoCreate : function(){
25252         
25253         var cfg = {
25254             tag : 'div',
25255             cls : 'small-box ',
25256             cn : [
25257                 {
25258                     tag : 'div',
25259                     cls : 'inner',
25260                     cn :[
25261                         {
25262                             tag : 'h3',
25263                             cls : 'roo-headline',
25264                             html : this.headline
25265                         },
25266                         {
25267                             tag : 'p',
25268                             cls : 'roo-content',
25269                             html : this.content
25270                         }
25271                     ]
25272                 }
25273             ]
25274         };
25275         
25276         if(this.icon){
25277             cfg.cn.push({
25278                 tag : 'div',
25279                 cls : 'icon',
25280                 cn :[
25281                     {
25282                         tag : 'i',
25283                         cls : 'ion ' + this.icon
25284                     }
25285                 ]
25286             });
25287         }
25288         
25289         if(this.footer){
25290             var footer = {
25291                 tag : 'a',
25292                 cls : 'small-box-footer',
25293                 href : this.fhref || '#',
25294                 html : this.footer
25295             };
25296             
25297             cfg.cn.push(footer);
25298             
25299         }
25300         
25301         return  cfg;
25302     },
25303
25304     onRender : function(ct,position){
25305         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25306
25307
25308        
25309                 
25310     },
25311
25312     setHeadline: function (value)
25313     {
25314         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25315     },
25316     
25317     setFooter: function (value, href)
25318     {
25319         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25320         
25321         if(href){
25322             this.el.select('a.small-box-footer',true).first().attr('href', href);
25323         }
25324         
25325     },
25326
25327     setContent: function (value)
25328     {
25329         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25330     },
25331
25332     initEvents: function() 
25333     {   
25334         
25335     }
25336     
25337 });
25338
25339  
25340 /*
25341  * - LGPL
25342  *
25343  * TabBox
25344  * 
25345  */
25346 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25347
25348 /**
25349  * @class Roo.bootstrap.dash.TabBox
25350  * @extends Roo.bootstrap.Component
25351  * Bootstrap TabBox class
25352  * @cfg {String} title Title of the TabBox
25353  * @cfg {String} icon Icon of the TabBox
25354  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25355  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25356  * 
25357  * @constructor
25358  * Create a new TabBox
25359  * @param {Object} config The config object
25360  */
25361
25362
25363 Roo.bootstrap.dash.TabBox = function(config){
25364     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25365     this.addEvents({
25366         // raw events
25367         /**
25368          * @event addpane
25369          * When a pane is added
25370          * @param {Roo.bootstrap.dash.TabPane} pane
25371          */
25372         "addpane" : true,
25373         /**
25374          * @event activatepane
25375          * When a pane is activated
25376          * @param {Roo.bootstrap.dash.TabPane} pane
25377          */
25378         "activatepane" : true
25379         
25380          
25381     });
25382     
25383     this.panes = [];
25384 };
25385
25386 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25387
25388     title : '',
25389     icon : false,
25390     showtabs : true,
25391     tabScrollable : false,
25392     
25393     getChildContainer : function()
25394     {
25395         return this.el.select('.tab-content', true).first();
25396     },
25397     
25398     getAutoCreate : function(){
25399         
25400         var header = {
25401             tag: 'li',
25402             cls: 'pull-left header',
25403             html: this.title,
25404             cn : []
25405         };
25406         
25407         if(this.icon){
25408             header.cn.push({
25409                 tag: 'i',
25410                 cls: 'fa ' + this.icon
25411             });
25412         }
25413         
25414         var h = {
25415             tag: 'ul',
25416             cls: 'nav nav-tabs pull-right',
25417             cn: [
25418                 header
25419             ]
25420         };
25421         
25422         if(this.tabScrollable){
25423             h = {
25424                 tag: 'div',
25425                 cls: 'tab-header',
25426                 cn: [
25427                     {
25428                         tag: 'ul',
25429                         cls: 'nav nav-tabs pull-right',
25430                         cn: [
25431                             header
25432                         ]
25433                     }
25434                 ]
25435             };
25436         }
25437         
25438         var cfg = {
25439             tag: 'div',
25440             cls: 'nav-tabs-custom',
25441             cn: [
25442                 h,
25443                 {
25444                     tag: 'div',
25445                     cls: 'tab-content no-padding',
25446                     cn: []
25447                 }
25448             ]
25449         };
25450
25451         return  cfg;
25452     },
25453     initEvents : function()
25454     {
25455         //Roo.log('add add pane handler');
25456         this.on('addpane', this.onAddPane, this);
25457     },
25458      /**
25459      * Updates the box title
25460      * @param {String} html to set the title to.
25461      */
25462     setTitle : function(value)
25463     {
25464         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25465     },
25466     onAddPane : function(pane)
25467     {
25468         this.panes.push(pane);
25469         //Roo.log('addpane');
25470         //Roo.log(pane);
25471         // tabs are rendere left to right..
25472         if(!this.showtabs){
25473             return;
25474         }
25475         
25476         var ctr = this.el.select('.nav-tabs', true).first();
25477          
25478          
25479         var existing = ctr.select('.nav-tab',true);
25480         var qty = existing.getCount();;
25481         
25482         
25483         var tab = ctr.createChild({
25484             tag : 'li',
25485             cls : 'nav-tab' + (qty ? '' : ' active'),
25486             cn : [
25487                 {
25488                     tag : 'a',
25489                     href:'#',
25490                     html : pane.title
25491                 }
25492             ]
25493         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25494         pane.tab = tab;
25495         
25496         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25497         if (!qty) {
25498             pane.el.addClass('active');
25499         }
25500         
25501                 
25502     },
25503     onTabClick : function(ev,un,ob,pane)
25504     {
25505         //Roo.log('tab - prev default');
25506         ev.preventDefault();
25507         
25508         
25509         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25510         pane.tab.addClass('active');
25511         //Roo.log(pane.title);
25512         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25513         // technically we should have a deactivate event.. but maybe add later.
25514         // and it should not de-activate the selected tab...
25515         this.fireEvent('activatepane', pane);
25516         pane.el.addClass('active');
25517         pane.fireEvent('activate');
25518         
25519         
25520     },
25521     
25522     getActivePane : function()
25523     {
25524         var r = false;
25525         Roo.each(this.panes, function(p) {
25526             if(p.el.hasClass('active')){
25527                 r = p;
25528                 return false;
25529             }
25530             
25531             return;
25532         });
25533         
25534         return r;
25535     }
25536     
25537     
25538 });
25539
25540  
25541 /*
25542  * - LGPL
25543  *
25544  * Tab pane
25545  * 
25546  */
25547 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25548 /**
25549  * @class Roo.bootstrap.TabPane
25550  * @extends Roo.bootstrap.Component
25551  * Bootstrap TabPane class
25552  * @cfg {Boolean} active (false | true) Default false
25553  * @cfg {String} title title of panel
25554
25555  * 
25556  * @constructor
25557  * Create a new TabPane
25558  * @param {Object} config The config object
25559  */
25560
25561 Roo.bootstrap.dash.TabPane = function(config){
25562     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25563     
25564     this.addEvents({
25565         // raw events
25566         /**
25567          * @event activate
25568          * When a pane is activated
25569          * @param {Roo.bootstrap.dash.TabPane} pane
25570          */
25571         "activate" : true
25572          
25573     });
25574 };
25575
25576 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25577     
25578     active : false,
25579     title : '',
25580     
25581     // the tabBox that this is attached to.
25582     tab : false,
25583      
25584     getAutoCreate : function() 
25585     {
25586         var cfg = {
25587             tag: 'div',
25588             cls: 'tab-pane'
25589         };
25590         
25591         if(this.active){
25592             cfg.cls += ' active';
25593         }
25594         
25595         return cfg;
25596     },
25597     initEvents  : function()
25598     {
25599         //Roo.log('trigger add pane handler');
25600         this.parent().fireEvent('addpane', this)
25601     },
25602     
25603      /**
25604      * Updates the tab title 
25605      * @param {String} html to set the title to.
25606      */
25607     setTitle: function(str)
25608     {
25609         if (!this.tab) {
25610             return;
25611         }
25612         this.title = str;
25613         this.tab.select('a', true).first().dom.innerHTML = str;
25614         
25615     }
25616     
25617     
25618     
25619 });
25620
25621  
25622
25623
25624  /*
25625  * - LGPL
25626  *
25627  * menu
25628  * 
25629  */
25630 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25631
25632 /**
25633  * @class Roo.bootstrap.menu.Menu
25634  * @extends Roo.bootstrap.Component
25635  * Bootstrap Menu class - container for Menu
25636  * @cfg {String} html Text of the menu
25637  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25638  * @cfg {String} icon Font awesome icon
25639  * @cfg {String} pos Menu align to (top | bottom) default bottom
25640  * 
25641  * 
25642  * @constructor
25643  * Create a new Menu
25644  * @param {Object} config The config object
25645  */
25646
25647
25648 Roo.bootstrap.menu.Menu = function(config){
25649     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25650     
25651     this.addEvents({
25652         /**
25653          * @event beforeshow
25654          * Fires before this menu is displayed
25655          * @param {Roo.bootstrap.menu.Menu} this
25656          */
25657         beforeshow : true,
25658         /**
25659          * @event beforehide
25660          * Fires before this menu is hidden
25661          * @param {Roo.bootstrap.menu.Menu} this
25662          */
25663         beforehide : true,
25664         /**
25665          * @event show
25666          * Fires after this menu is displayed
25667          * @param {Roo.bootstrap.menu.Menu} this
25668          */
25669         show : true,
25670         /**
25671          * @event hide
25672          * Fires after this menu is hidden
25673          * @param {Roo.bootstrap.menu.Menu} this
25674          */
25675         hide : true,
25676         /**
25677          * @event click
25678          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25679          * @param {Roo.bootstrap.menu.Menu} this
25680          * @param {Roo.EventObject} e
25681          */
25682         click : true
25683     });
25684     
25685 };
25686
25687 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25688     
25689     submenu : false,
25690     html : '',
25691     weight : 'default',
25692     icon : false,
25693     pos : 'bottom',
25694     
25695     
25696     getChildContainer : function() {
25697         if(this.isSubMenu){
25698             return this.el;
25699         }
25700         
25701         return this.el.select('ul.dropdown-menu', true).first();  
25702     },
25703     
25704     getAutoCreate : function()
25705     {
25706         var text = [
25707             {
25708                 tag : 'span',
25709                 cls : 'roo-menu-text',
25710                 html : this.html
25711             }
25712         ];
25713         
25714         if(this.icon){
25715             text.unshift({
25716                 tag : 'i',
25717                 cls : 'fa ' + this.icon
25718             })
25719         }
25720         
25721         
25722         var cfg = {
25723             tag : 'div',
25724             cls : 'btn-group',
25725             cn : [
25726                 {
25727                     tag : 'button',
25728                     cls : 'dropdown-button btn btn-' + this.weight,
25729                     cn : text
25730                 },
25731                 {
25732                     tag : 'button',
25733                     cls : 'dropdown-toggle btn btn-' + this.weight,
25734                     cn : [
25735                         {
25736                             tag : 'span',
25737                             cls : 'caret'
25738                         }
25739                     ]
25740                 },
25741                 {
25742                     tag : 'ul',
25743                     cls : 'dropdown-menu'
25744                 }
25745             ]
25746             
25747         };
25748         
25749         if(this.pos == 'top'){
25750             cfg.cls += ' dropup';
25751         }
25752         
25753         if(this.isSubMenu){
25754             cfg = {
25755                 tag : 'ul',
25756                 cls : 'dropdown-menu'
25757             }
25758         }
25759         
25760         return cfg;
25761     },
25762     
25763     onRender : function(ct, position)
25764     {
25765         this.isSubMenu = ct.hasClass('dropdown-submenu');
25766         
25767         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25768     },
25769     
25770     initEvents : function() 
25771     {
25772         if(this.isSubMenu){
25773             return;
25774         }
25775         
25776         this.hidden = true;
25777         
25778         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25779         this.triggerEl.on('click', this.onTriggerPress, this);
25780         
25781         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25782         this.buttonEl.on('click', this.onClick, this);
25783         
25784     },
25785     
25786     list : function()
25787     {
25788         if(this.isSubMenu){
25789             return this.el;
25790         }
25791         
25792         return this.el.select('ul.dropdown-menu', true).first();
25793     },
25794     
25795     onClick : function(e)
25796     {
25797         this.fireEvent("click", this, e);
25798     },
25799     
25800     onTriggerPress  : function(e)
25801     {   
25802         if (this.isVisible()) {
25803             this.hide();
25804         } else {
25805             this.show();
25806         }
25807     },
25808     
25809     isVisible : function(){
25810         return !this.hidden;
25811     },
25812     
25813     show : function()
25814     {
25815         this.fireEvent("beforeshow", this);
25816         
25817         this.hidden = false;
25818         this.el.addClass('open');
25819         
25820         Roo.get(document).on("mouseup", this.onMouseUp, this);
25821         
25822         this.fireEvent("show", this);
25823         
25824         
25825     },
25826     
25827     hide : function()
25828     {
25829         this.fireEvent("beforehide", this);
25830         
25831         this.hidden = true;
25832         this.el.removeClass('open');
25833         
25834         Roo.get(document).un("mouseup", this.onMouseUp);
25835         
25836         this.fireEvent("hide", this);
25837     },
25838     
25839     onMouseUp : function()
25840     {
25841         this.hide();
25842     }
25843     
25844 });
25845
25846  
25847  /*
25848  * - LGPL
25849  *
25850  * menu item
25851  * 
25852  */
25853 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25854
25855 /**
25856  * @class Roo.bootstrap.menu.Item
25857  * @extends Roo.bootstrap.Component
25858  * Bootstrap MenuItem class
25859  * @cfg {Boolean} submenu (true | false) default false
25860  * @cfg {String} html text of the item
25861  * @cfg {String} href the link
25862  * @cfg {Boolean} disable (true | false) default false
25863  * @cfg {Boolean} preventDefault (true | false) default true
25864  * @cfg {String} icon Font awesome icon
25865  * @cfg {String} pos Submenu align to (left | right) default right 
25866  * 
25867  * 
25868  * @constructor
25869  * Create a new Item
25870  * @param {Object} config The config object
25871  */
25872
25873
25874 Roo.bootstrap.menu.Item = function(config){
25875     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25876     this.addEvents({
25877         /**
25878          * @event mouseover
25879          * Fires when the mouse is hovering over this menu
25880          * @param {Roo.bootstrap.menu.Item} this
25881          * @param {Roo.EventObject} e
25882          */
25883         mouseover : true,
25884         /**
25885          * @event mouseout
25886          * Fires when the mouse exits this menu
25887          * @param {Roo.bootstrap.menu.Item} this
25888          * @param {Roo.EventObject} e
25889          */
25890         mouseout : true,
25891         // raw events
25892         /**
25893          * @event click
25894          * The raw click event for the entire grid.
25895          * @param {Roo.EventObject} e
25896          */
25897         click : true
25898     });
25899 };
25900
25901 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25902     
25903     submenu : false,
25904     href : '',
25905     html : '',
25906     preventDefault: true,
25907     disable : false,
25908     icon : false,
25909     pos : 'right',
25910     
25911     getAutoCreate : function()
25912     {
25913         var text = [
25914             {
25915                 tag : 'span',
25916                 cls : 'roo-menu-item-text',
25917                 html : this.html
25918             }
25919         ];
25920         
25921         if(this.icon){
25922             text.unshift({
25923                 tag : 'i',
25924                 cls : 'fa ' + this.icon
25925             })
25926         }
25927         
25928         var cfg = {
25929             tag : 'li',
25930             cn : [
25931                 {
25932                     tag : 'a',
25933                     href : this.href || '#',
25934                     cn : text
25935                 }
25936             ]
25937         };
25938         
25939         if(this.disable){
25940             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25941         }
25942         
25943         if(this.submenu){
25944             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25945             
25946             if(this.pos == 'left'){
25947                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25948             }
25949         }
25950         
25951         return cfg;
25952     },
25953     
25954     initEvents : function() 
25955     {
25956         this.el.on('mouseover', this.onMouseOver, this);
25957         this.el.on('mouseout', this.onMouseOut, this);
25958         
25959         this.el.select('a', true).first().on('click', this.onClick, this);
25960         
25961     },
25962     
25963     onClick : function(e)
25964     {
25965         if(this.preventDefault){
25966             e.preventDefault();
25967         }
25968         
25969         this.fireEvent("click", this, e);
25970     },
25971     
25972     onMouseOver : function(e)
25973     {
25974         if(this.submenu && this.pos == 'left'){
25975             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25976         }
25977         
25978         this.fireEvent("mouseover", this, e);
25979     },
25980     
25981     onMouseOut : function(e)
25982     {
25983         this.fireEvent("mouseout", this, e);
25984     }
25985 });
25986
25987  
25988
25989  /*
25990  * - LGPL
25991  *
25992  * menu separator
25993  * 
25994  */
25995 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25996
25997 /**
25998  * @class Roo.bootstrap.menu.Separator
25999  * @extends Roo.bootstrap.Component
26000  * Bootstrap Separator class
26001  * 
26002  * @constructor
26003  * Create a new Separator
26004  * @param {Object} config The config object
26005  */
26006
26007
26008 Roo.bootstrap.menu.Separator = function(config){
26009     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26010 };
26011
26012 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26013     
26014     getAutoCreate : function(){
26015         var cfg = {
26016             tag : 'li',
26017             cls: 'divider'
26018         };
26019         
26020         return cfg;
26021     }
26022    
26023 });
26024
26025  
26026
26027  /*
26028  * - LGPL
26029  *
26030  * Tooltip
26031  * 
26032  */
26033
26034 /**
26035  * @class Roo.bootstrap.Tooltip
26036  * Bootstrap Tooltip class
26037  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26038  * to determine which dom element triggers the tooltip.
26039  * 
26040  * It needs to add support for additional attributes like tooltip-position
26041  * 
26042  * @constructor
26043  * Create a new Toolti
26044  * @param {Object} config The config object
26045  */
26046
26047 Roo.bootstrap.Tooltip = function(config){
26048     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26049     
26050     this.alignment = Roo.bootstrap.Tooltip.alignment;
26051     
26052     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26053         this.alignment = config.alignment;
26054     }
26055     
26056 };
26057
26058 Roo.apply(Roo.bootstrap.Tooltip, {
26059     /**
26060      * @function init initialize tooltip monitoring.
26061      * @static
26062      */
26063     currentEl : false,
26064     currentTip : false,
26065     currentRegion : false,
26066     
26067     //  init : delay?
26068     
26069     init : function()
26070     {
26071         Roo.get(document).on('mouseover', this.enter ,this);
26072         Roo.get(document).on('mouseout', this.leave, this);
26073          
26074         
26075         this.currentTip = new Roo.bootstrap.Tooltip();
26076     },
26077     
26078     enter : function(ev)
26079     {
26080         var dom = ev.getTarget();
26081         
26082         //Roo.log(['enter',dom]);
26083         var el = Roo.fly(dom);
26084         if (this.currentEl) {
26085             //Roo.log(dom);
26086             //Roo.log(this.currentEl);
26087             //Roo.log(this.currentEl.contains(dom));
26088             if (this.currentEl == el) {
26089                 return;
26090             }
26091             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26092                 return;
26093             }
26094
26095         }
26096         
26097         if (this.currentTip.el) {
26098             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26099         }    
26100         //Roo.log(ev);
26101         
26102         if(!el || el.dom == document){
26103             return;
26104         }
26105         
26106         var bindEl = el;
26107         
26108         // you can not look for children, as if el is the body.. then everythign is the child..
26109         if (!el.attr('tooltip')) { //
26110             if (!el.select("[tooltip]").elements.length) {
26111                 return;
26112             }
26113             // is the mouse over this child...?
26114             bindEl = el.select("[tooltip]").first();
26115             var xy = ev.getXY();
26116             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26117                 //Roo.log("not in region.");
26118                 return;
26119             }
26120             //Roo.log("child element over..");
26121             
26122         }
26123         this.currentEl = bindEl;
26124         this.currentTip.bind(bindEl);
26125         this.currentRegion = Roo.lib.Region.getRegion(dom);
26126         this.currentTip.enter();
26127         
26128     },
26129     leave : function(ev)
26130     {
26131         var dom = ev.getTarget();
26132         //Roo.log(['leave',dom]);
26133         if (!this.currentEl) {
26134             return;
26135         }
26136         
26137         
26138         if (dom != this.currentEl.dom) {
26139             return;
26140         }
26141         var xy = ev.getXY();
26142         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26143             return;
26144         }
26145         // only activate leave if mouse cursor is outside... bounding box..
26146         
26147         
26148         
26149         
26150         if (this.currentTip) {
26151             this.currentTip.leave();
26152         }
26153         //Roo.log('clear currentEl');
26154         this.currentEl = false;
26155         
26156         
26157     },
26158     alignment : {
26159         'left' : ['r-l', [-2,0], 'right'],
26160         'right' : ['l-r', [2,0], 'left'],
26161         'bottom' : ['t-b', [0,2], 'top'],
26162         'top' : [ 'b-t', [0,-2], 'bottom']
26163     }
26164     
26165 });
26166
26167
26168 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26169     
26170     
26171     bindEl : false,
26172     
26173     delay : null, // can be { show : 300 , hide: 500}
26174     
26175     timeout : null,
26176     
26177     hoverState : null, //???
26178     
26179     placement : 'bottom', 
26180     
26181     alignment : false,
26182     
26183     getAutoCreate : function(){
26184     
26185         var cfg = {
26186            cls : 'tooltip',
26187            role : 'tooltip',
26188            cn : [
26189                 {
26190                     cls : 'tooltip-arrow'
26191                 },
26192                 {
26193                     cls : 'tooltip-inner'
26194                 }
26195            ]
26196         };
26197         
26198         return cfg;
26199     },
26200     bind : function(el)
26201     {
26202         this.bindEl = el;
26203     },
26204       
26205     
26206     enter : function () {
26207        
26208         if (this.timeout != null) {
26209             clearTimeout(this.timeout);
26210         }
26211         
26212         this.hoverState = 'in';
26213          //Roo.log("enter - show");
26214         if (!this.delay || !this.delay.show) {
26215             this.show();
26216             return;
26217         }
26218         var _t = this;
26219         this.timeout = setTimeout(function () {
26220             if (_t.hoverState == 'in') {
26221                 _t.show();
26222             }
26223         }, this.delay.show);
26224     },
26225     leave : function()
26226     {
26227         clearTimeout(this.timeout);
26228     
26229         this.hoverState = 'out';
26230          if (!this.delay || !this.delay.hide) {
26231             this.hide();
26232             return;
26233         }
26234        
26235         var _t = this;
26236         this.timeout = setTimeout(function () {
26237             //Roo.log("leave - timeout");
26238             
26239             if (_t.hoverState == 'out') {
26240                 _t.hide();
26241                 Roo.bootstrap.Tooltip.currentEl = false;
26242             }
26243         }, delay);
26244     },
26245     
26246     show : function (msg)
26247     {
26248         if (!this.el) {
26249             this.render(document.body);
26250         }
26251         // set content.
26252         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26253         
26254         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26255         
26256         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26257         
26258         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26259         
26260         var placement = typeof this.placement == 'function' ?
26261             this.placement.call(this, this.el, on_el) :
26262             this.placement;
26263             
26264         var autoToken = /\s?auto?\s?/i;
26265         var autoPlace = autoToken.test(placement);
26266         if (autoPlace) {
26267             placement = placement.replace(autoToken, '') || 'top';
26268         }
26269         
26270         //this.el.detach()
26271         //this.el.setXY([0,0]);
26272         this.el.show();
26273         //this.el.dom.style.display='block';
26274         
26275         //this.el.appendTo(on_el);
26276         
26277         var p = this.getPosition();
26278         var box = this.el.getBox();
26279         
26280         if (autoPlace) {
26281             // fixme..
26282         }
26283         
26284         var align = this.alignment[placement];
26285         
26286         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26287         
26288         if(placement == 'top' || placement == 'bottom'){
26289             if(xy[0] < 0){
26290                 placement = 'right';
26291             }
26292             
26293             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26294                 placement = 'left';
26295             }
26296             
26297             var scroll = Roo.select('body', true).first().getScroll();
26298             
26299             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26300                 placement = 'top';
26301             }
26302             
26303             align = this.alignment[placement];
26304         }
26305         
26306         this.el.alignTo(this.bindEl, align[0],align[1]);
26307         //var arrow = this.el.select('.arrow',true).first();
26308         //arrow.set(align[2], 
26309         
26310         this.el.addClass(placement);
26311         
26312         this.el.addClass('in fade');
26313         
26314         this.hoverState = null;
26315         
26316         if (this.el.hasClass('fade')) {
26317             // fade it?
26318         }
26319         
26320     },
26321     hide : function()
26322     {
26323          
26324         if (!this.el) {
26325             return;
26326         }
26327         //this.el.setXY([0,0]);
26328         this.el.removeClass('in');
26329         //this.el.hide();
26330         
26331     }
26332     
26333 });
26334  
26335
26336  /*
26337  * - LGPL
26338  *
26339  * Location Picker
26340  * 
26341  */
26342
26343 /**
26344  * @class Roo.bootstrap.LocationPicker
26345  * @extends Roo.bootstrap.Component
26346  * Bootstrap LocationPicker class
26347  * @cfg {Number} latitude Position when init default 0
26348  * @cfg {Number} longitude Position when init default 0
26349  * @cfg {Number} zoom default 15
26350  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26351  * @cfg {Boolean} mapTypeControl default false
26352  * @cfg {Boolean} disableDoubleClickZoom default false
26353  * @cfg {Boolean} scrollwheel default true
26354  * @cfg {Boolean} streetViewControl default false
26355  * @cfg {Number} radius default 0
26356  * @cfg {String} locationName
26357  * @cfg {Boolean} draggable default true
26358  * @cfg {Boolean} enableAutocomplete default false
26359  * @cfg {Boolean} enableReverseGeocode default true
26360  * @cfg {String} markerTitle
26361  * 
26362  * @constructor
26363  * Create a new LocationPicker
26364  * @param {Object} config The config object
26365  */
26366
26367
26368 Roo.bootstrap.LocationPicker = function(config){
26369     
26370     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26371     
26372     this.addEvents({
26373         /**
26374          * @event initial
26375          * Fires when the picker initialized.
26376          * @param {Roo.bootstrap.LocationPicker} this
26377          * @param {Google Location} location
26378          */
26379         initial : true,
26380         /**
26381          * @event positionchanged
26382          * Fires when the picker position changed.
26383          * @param {Roo.bootstrap.LocationPicker} this
26384          * @param {Google Location} location
26385          */
26386         positionchanged : true,
26387         /**
26388          * @event resize
26389          * Fires when the map resize.
26390          * @param {Roo.bootstrap.LocationPicker} this
26391          */
26392         resize : true,
26393         /**
26394          * @event show
26395          * Fires when the map show.
26396          * @param {Roo.bootstrap.LocationPicker} this
26397          */
26398         show : true,
26399         /**
26400          * @event hide
26401          * Fires when the map hide.
26402          * @param {Roo.bootstrap.LocationPicker} this
26403          */
26404         hide : true,
26405         /**
26406          * @event mapClick
26407          * Fires when click the map.
26408          * @param {Roo.bootstrap.LocationPicker} this
26409          * @param {Map event} e
26410          */
26411         mapClick : true,
26412         /**
26413          * @event mapRightClick
26414          * Fires when right click the map.
26415          * @param {Roo.bootstrap.LocationPicker} this
26416          * @param {Map event} e
26417          */
26418         mapRightClick : true,
26419         /**
26420          * @event markerClick
26421          * Fires when click the marker.
26422          * @param {Roo.bootstrap.LocationPicker} this
26423          * @param {Map event} e
26424          */
26425         markerClick : true,
26426         /**
26427          * @event markerRightClick
26428          * Fires when right click the marker.
26429          * @param {Roo.bootstrap.LocationPicker} this
26430          * @param {Map event} e
26431          */
26432         markerRightClick : true,
26433         /**
26434          * @event OverlayViewDraw
26435          * Fires when OverlayView Draw
26436          * @param {Roo.bootstrap.LocationPicker} this
26437          */
26438         OverlayViewDraw : true,
26439         /**
26440          * @event OverlayViewOnAdd
26441          * Fires when OverlayView Draw
26442          * @param {Roo.bootstrap.LocationPicker} this
26443          */
26444         OverlayViewOnAdd : true,
26445         /**
26446          * @event OverlayViewOnRemove
26447          * Fires when OverlayView Draw
26448          * @param {Roo.bootstrap.LocationPicker} this
26449          */
26450         OverlayViewOnRemove : true,
26451         /**
26452          * @event OverlayViewShow
26453          * Fires when OverlayView Draw
26454          * @param {Roo.bootstrap.LocationPicker} this
26455          * @param {Pixel} cpx
26456          */
26457         OverlayViewShow : true,
26458         /**
26459          * @event OverlayViewHide
26460          * Fires when OverlayView Draw
26461          * @param {Roo.bootstrap.LocationPicker} this
26462          */
26463         OverlayViewHide : true,
26464         /**
26465          * @event loadexception
26466          * Fires when load google lib failed.
26467          * @param {Roo.bootstrap.LocationPicker} this
26468          */
26469         loadexception : true
26470     });
26471         
26472 };
26473
26474 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26475     
26476     gMapContext: false,
26477     
26478     latitude: 0,
26479     longitude: 0,
26480     zoom: 15,
26481     mapTypeId: false,
26482     mapTypeControl: false,
26483     disableDoubleClickZoom: false,
26484     scrollwheel: true,
26485     streetViewControl: false,
26486     radius: 0,
26487     locationName: '',
26488     draggable: true,
26489     enableAutocomplete: false,
26490     enableReverseGeocode: true,
26491     markerTitle: '',
26492     
26493     getAutoCreate: function()
26494     {
26495
26496         var cfg = {
26497             tag: 'div',
26498             cls: 'roo-location-picker'
26499         };
26500         
26501         return cfg
26502     },
26503     
26504     initEvents: function(ct, position)
26505     {       
26506         if(!this.el.getWidth() || this.isApplied()){
26507             return;
26508         }
26509         
26510         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26511         
26512         this.initial();
26513     },
26514     
26515     initial: function()
26516     {
26517         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26518             this.fireEvent('loadexception', this);
26519             return;
26520         }
26521         
26522         if(!this.mapTypeId){
26523             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26524         }
26525         
26526         this.gMapContext = this.GMapContext();
26527         
26528         this.initOverlayView();
26529         
26530         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26531         
26532         var _this = this;
26533                 
26534         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26535             _this.setPosition(_this.gMapContext.marker.position);
26536         });
26537         
26538         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26539             _this.fireEvent('mapClick', this, event);
26540             
26541         });
26542
26543         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26544             _this.fireEvent('mapRightClick', this, event);
26545             
26546         });
26547         
26548         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26549             _this.fireEvent('markerClick', this, event);
26550             
26551         });
26552
26553         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26554             _this.fireEvent('markerRightClick', this, event);
26555             
26556         });
26557         
26558         this.setPosition(this.gMapContext.location);
26559         
26560         this.fireEvent('initial', this, this.gMapContext.location);
26561     },
26562     
26563     initOverlayView: function()
26564     {
26565         var _this = this;
26566         
26567         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26568             
26569             draw: function()
26570             {
26571                 _this.fireEvent('OverlayViewDraw', _this);
26572             },
26573             
26574             onAdd: function()
26575             {
26576                 _this.fireEvent('OverlayViewOnAdd', _this);
26577             },
26578             
26579             onRemove: function()
26580             {
26581                 _this.fireEvent('OverlayViewOnRemove', _this);
26582             },
26583             
26584             show: function(cpx)
26585             {
26586                 _this.fireEvent('OverlayViewShow', _this, cpx);
26587             },
26588             
26589             hide: function()
26590             {
26591                 _this.fireEvent('OverlayViewHide', _this);
26592             }
26593             
26594         });
26595     },
26596     
26597     fromLatLngToContainerPixel: function(event)
26598     {
26599         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26600     },
26601     
26602     isApplied: function() 
26603     {
26604         return this.getGmapContext() == false ? false : true;
26605     },
26606     
26607     getGmapContext: function() 
26608     {
26609         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26610     },
26611     
26612     GMapContext: function() 
26613     {
26614         var position = new google.maps.LatLng(this.latitude, this.longitude);
26615         
26616         var _map = new google.maps.Map(this.el.dom, {
26617             center: position,
26618             zoom: this.zoom,
26619             mapTypeId: this.mapTypeId,
26620             mapTypeControl: this.mapTypeControl,
26621             disableDoubleClickZoom: this.disableDoubleClickZoom,
26622             scrollwheel: this.scrollwheel,
26623             streetViewControl: this.streetViewControl,
26624             locationName: this.locationName,
26625             draggable: this.draggable,
26626             enableAutocomplete: this.enableAutocomplete,
26627             enableReverseGeocode: this.enableReverseGeocode
26628         });
26629         
26630         var _marker = new google.maps.Marker({
26631             position: position,
26632             map: _map,
26633             title: this.markerTitle,
26634             draggable: this.draggable
26635         });
26636         
26637         return {
26638             map: _map,
26639             marker: _marker,
26640             circle: null,
26641             location: position,
26642             radius: this.radius,
26643             locationName: this.locationName,
26644             addressComponents: {
26645                 formatted_address: null,
26646                 addressLine1: null,
26647                 addressLine2: null,
26648                 streetName: null,
26649                 streetNumber: null,
26650                 city: null,
26651                 district: null,
26652                 state: null,
26653                 stateOrProvince: null
26654             },
26655             settings: this,
26656             domContainer: this.el.dom,
26657             geodecoder: new google.maps.Geocoder()
26658         };
26659     },
26660     
26661     drawCircle: function(center, radius, options) 
26662     {
26663         if (this.gMapContext.circle != null) {
26664             this.gMapContext.circle.setMap(null);
26665         }
26666         if (radius > 0) {
26667             radius *= 1;
26668             options = Roo.apply({}, options, {
26669                 strokeColor: "#0000FF",
26670                 strokeOpacity: .35,
26671                 strokeWeight: 2,
26672                 fillColor: "#0000FF",
26673                 fillOpacity: .2
26674             });
26675             
26676             options.map = this.gMapContext.map;
26677             options.radius = radius;
26678             options.center = center;
26679             this.gMapContext.circle = new google.maps.Circle(options);
26680             return this.gMapContext.circle;
26681         }
26682         
26683         return null;
26684     },
26685     
26686     setPosition: function(location) 
26687     {
26688         this.gMapContext.location = location;
26689         this.gMapContext.marker.setPosition(location);
26690         this.gMapContext.map.panTo(location);
26691         this.drawCircle(location, this.gMapContext.radius, {});
26692         
26693         var _this = this;
26694         
26695         if (this.gMapContext.settings.enableReverseGeocode) {
26696             this.gMapContext.geodecoder.geocode({
26697                 latLng: this.gMapContext.location
26698             }, function(results, status) {
26699                 
26700                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26701                     _this.gMapContext.locationName = results[0].formatted_address;
26702                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26703                     
26704                     _this.fireEvent('positionchanged', this, location);
26705                 }
26706             });
26707             
26708             return;
26709         }
26710         
26711         this.fireEvent('positionchanged', this, location);
26712     },
26713     
26714     resize: function()
26715     {
26716         google.maps.event.trigger(this.gMapContext.map, "resize");
26717         
26718         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26719         
26720         this.fireEvent('resize', this);
26721     },
26722     
26723     setPositionByLatLng: function(latitude, longitude)
26724     {
26725         this.setPosition(new google.maps.LatLng(latitude, longitude));
26726     },
26727     
26728     getCurrentPosition: function() 
26729     {
26730         return {
26731             latitude: this.gMapContext.location.lat(),
26732             longitude: this.gMapContext.location.lng()
26733         };
26734     },
26735     
26736     getAddressName: function() 
26737     {
26738         return this.gMapContext.locationName;
26739     },
26740     
26741     getAddressComponents: function() 
26742     {
26743         return this.gMapContext.addressComponents;
26744     },
26745     
26746     address_component_from_google_geocode: function(address_components) 
26747     {
26748         var result = {};
26749         
26750         for (var i = 0; i < address_components.length; i++) {
26751             var component = address_components[i];
26752             if (component.types.indexOf("postal_code") >= 0) {
26753                 result.postalCode = component.short_name;
26754             } else if (component.types.indexOf("street_number") >= 0) {
26755                 result.streetNumber = component.short_name;
26756             } else if (component.types.indexOf("route") >= 0) {
26757                 result.streetName = component.short_name;
26758             } else if (component.types.indexOf("neighborhood") >= 0) {
26759                 result.city = component.short_name;
26760             } else if (component.types.indexOf("locality") >= 0) {
26761                 result.city = component.short_name;
26762             } else if (component.types.indexOf("sublocality") >= 0) {
26763                 result.district = component.short_name;
26764             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26765                 result.stateOrProvince = component.short_name;
26766             } else if (component.types.indexOf("country") >= 0) {
26767                 result.country = component.short_name;
26768             }
26769         }
26770         
26771         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26772         result.addressLine2 = "";
26773         return result;
26774     },
26775     
26776     setZoomLevel: function(zoom)
26777     {
26778         this.gMapContext.map.setZoom(zoom);
26779     },
26780     
26781     show: function()
26782     {
26783         if(!this.el){
26784             return;
26785         }
26786         
26787         this.el.show();
26788         
26789         this.resize();
26790         
26791         this.fireEvent('show', this);
26792     },
26793     
26794     hide: function()
26795     {
26796         if(!this.el){
26797             return;
26798         }
26799         
26800         this.el.hide();
26801         
26802         this.fireEvent('hide', this);
26803     }
26804     
26805 });
26806
26807 Roo.apply(Roo.bootstrap.LocationPicker, {
26808     
26809     OverlayView : function(map, options)
26810     {
26811         options = options || {};
26812         
26813         this.setMap(map);
26814     }
26815     
26816     
26817 });/*
26818  * - LGPL
26819  *
26820  * Alert
26821  * 
26822  */
26823
26824 /**
26825  * @class Roo.bootstrap.Alert
26826  * @extends Roo.bootstrap.Component
26827  * Bootstrap Alert class
26828  * @cfg {String} title The title of alert
26829  * @cfg {String} html The content of alert
26830  * @cfg {String} weight (  success | info | warning | danger )
26831  * @cfg {String} faicon font-awesomeicon
26832  * 
26833  * @constructor
26834  * Create a new alert
26835  * @param {Object} config The config object
26836  */
26837
26838
26839 Roo.bootstrap.Alert = function(config){
26840     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26841     
26842 };
26843
26844 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26845     
26846     title: '',
26847     html: '',
26848     weight: false,
26849     faicon: false,
26850     
26851     getAutoCreate : function()
26852     {
26853         
26854         var cfg = {
26855             tag : 'div',
26856             cls : 'alert',
26857             cn : [
26858                 {
26859                     tag : 'i',
26860                     cls : 'roo-alert-icon'
26861                     
26862                 },
26863                 {
26864                     tag : 'b',
26865                     cls : 'roo-alert-title',
26866                     html : this.title
26867                 },
26868                 {
26869                     tag : 'span',
26870                     cls : 'roo-alert-text',
26871                     html : this.html
26872                 }
26873             ]
26874         };
26875         
26876         if(this.faicon){
26877             cfg.cn[0].cls += ' fa ' + this.faicon;
26878         }
26879         
26880         if(this.weight){
26881             cfg.cls += ' alert-' + this.weight;
26882         }
26883         
26884         return cfg;
26885     },
26886     
26887     initEvents: function() 
26888     {
26889         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26890     },
26891     
26892     setTitle : function(str)
26893     {
26894         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26895     },
26896     
26897     setText : function(str)
26898     {
26899         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26900     },
26901     
26902     setWeight : function(weight)
26903     {
26904         if(this.weight){
26905             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26906         }
26907         
26908         this.weight = weight;
26909         
26910         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26911     },
26912     
26913     setIcon : function(icon)
26914     {
26915         if(this.faicon){
26916             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26917         }
26918         
26919         this.faicon = icon;
26920         
26921         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26922     },
26923     
26924     hide: function() 
26925     {
26926         this.el.hide();   
26927     },
26928     
26929     show: function() 
26930     {  
26931         this.el.show();   
26932     }
26933     
26934 });
26935
26936  
26937 /*
26938 * Licence: LGPL
26939 */
26940
26941 /**
26942  * @class Roo.bootstrap.UploadCropbox
26943  * @extends Roo.bootstrap.Component
26944  * Bootstrap UploadCropbox class
26945  * @cfg {String} emptyText show when image has been loaded
26946  * @cfg {String} rotateNotify show when image too small to rotate
26947  * @cfg {Number} errorTimeout default 3000
26948  * @cfg {Number} minWidth default 300
26949  * @cfg {Number} minHeight default 300
26950  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26951  * @cfg {Boolean} isDocument (true|false) default false
26952  * @cfg {String} url action url
26953  * @cfg {String} paramName default 'imageUpload'
26954  * @cfg {String} method default POST
26955  * @cfg {Boolean} loadMask (true|false) default true
26956  * @cfg {Boolean} loadingText default 'Loading...'
26957  * 
26958  * @constructor
26959  * Create a new UploadCropbox
26960  * @param {Object} config The config object
26961  */
26962
26963 Roo.bootstrap.UploadCropbox = function(config){
26964     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26965     
26966     this.addEvents({
26967         /**
26968          * @event beforeselectfile
26969          * Fire before select file
26970          * @param {Roo.bootstrap.UploadCropbox} this
26971          */
26972         "beforeselectfile" : true,
26973         /**
26974          * @event initial
26975          * Fire after initEvent
26976          * @param {Roo.bootstrap.UploadCropbox} this
26977          */
26978         "initial" : true,
26979         /**
26980          * @event crop
26981          * Fire after initEvent
26982          * @param {Roo.bootstrap.UploadCropbox} this
26983          * @param {String} data
26984          */
26985         "crop" : true,
26986         /**
26987          * @event prepare
26988          * Fire when preparing the file data
26989          * @param {Roo.bootstrap.UploadCropbox} this
26990          * @param {Object} file
26991          */
26992         "prepare" : true,
26993         /**
26994          * @event exception
26995          * Fire when get exception
26996          * @param {Roo.bootstrap.UploadCropbox} this
26997          * @param {XMLHttpRequest} xhr
26998          */
26999         "exception" : true,
27000         /**
27001          * @event beforeloadcanvas
27002          * Fire before load the canvas
27003          * @param {Roo.bootstrap.UploadCropbox} this
27004          * @param {String} src
27005          */
27006         "beforeloadcanvas" : true,
27007         /**
27008          * @event trash
27009          * Fire when trash image
27010          * @param {Roo.bootstrap.UploadCropbox} this
27011          */
27012         "trash" : true,
27013         /**
27014          * @event download
27015          * Fire when download the image
27016          * @param {Roo.bootstrap.UploadCropbox} this
27017          */
27018         "download" : true,
27019         /**
27020          * @event footerbuttonclick
27021          * Fire when footerbuttonclick
27022          * @param {Roo.bootstrap.UploadCropbox} this
27023          * @param {String} type
27024          */
27025         "footerbuttonclick" : true,
27026         /**
27027          * @event resize
27028          * Fire when resize
27029          * @param {Roo.bootstrap.UploadCropbox} this
27030          */
27031         "resize" : true,
27032         /**
27033          * @event rotate
27034          * Fire when rotate the image
27035          * @param {Roo.bootstrap.UploadCropbox} this
27036          * @param {String} pos
27037          */
27038         "rotate" : true,
27039         /**
27040          * @event inspect
27041          * Fire when inspect the file
27042          * @param {Roo.bootstrap.UploadCropbox} this
27043          * @param {Object} file
27044          */
27045         "inspect" : true,
27046         /**
27047          * @event upload
27048          * Fire when xhr upload the file
27049          * @param {Roo.bootstrap.UploadCropbox} this
27050          * @param {Object} data
27051          */
27052         "upload" : true,
27053         /**
27054          * @event arrange
27055          * Fire when arrange the file data
27056          * @param {Roo.bootstrap.UploadCropbox} this
27057          * @param {Object} formData
27058          */
27059         "arrange" : true
27060     });
27061     
27062     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27063 };
27064
27065 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27066     
27067     emptyText : 'Click to upload image',
27068     rotateNotify : 'Image is too small to rotate',
27069     errorTimeout : 3000,
27070     scale : 0,
27071     baseScale : 1,
27072     rotate : 0,
27073     dragable : false,
27074     pinching : false,
27075     mouseX : 0,
27076     mouseY : 0,
27077     cropData : false,
27078     minWidth : 300,
27079     minHeight : 300,
27080     file : false,
27081     exif : {},
27082     baseRotate : 1,
27083     cropType : 'image/jpeg',
27084     buttons : false,
27085     canvasLoaded : false,
27086     isDocument : false,
27087     method : 'POST',
27088     paramName : 'imageUpload',
27089     loadMask : true,
27090     loadingText : 'Loading...',
27091     maskEl : false,
27092     
27093     getAutoCreate : function()
27094     {
27095         var cfg = {
27096             tag : 'div',
27097             cls : 'roo-upload-cropbox',
27098             cn : [
27099                 {
27100                     tag : 'input',
27101                     cls : 'roo-upload-cropbox-selector',
27102                     type : 'file'
27103                 },
27104                 {
27105                     tag : 'div',
27106                     cls : 'roo-upload-cropbox-body',
27107                     style : 'cursor:pointer',
27108                     cn : [
27109                         {
27110                             tag : 'div',
27111                             cls : 'roo-upload-cropbox-preview'
27112                         },
27113                         {
27114                             tag : 'div',
27115                             cls : 'roo-upload-cropbox-thumb'
27116                         },
27117                         {
27118                             tag : 'div',
27119                             cls : 'roo-upload-cropbox-empty-notify',
27120                             html : this.emptyText
27121                         },
27122                         {
27123                             tag : 'div',
27124                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27125                             html : this.rotateNotify
27126                         }
27127                     ]
27128                 },
27129                 {
27130                     tag : 'div',
27131                     cls : 'roo-upload-cropbox-footer',
27132                     cn : {
27133                         tag : 'div',
27134                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27135                         cn : []
27136                     }
27137                 }
27138             ]
27139         };
27140         
27141         return cfg;
27142     },
27143     
27144     onRender : function(ct, position)
27145     {
27146         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27147         
27148         if (this.buttons.length) {
27149             
27150             Roo.each(this.buttons, function(bb) {
27151                 
27152                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27153                 
27154                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27155                 
27156             }, this);
27157         }
27158         
27159         if(this.loadMask){
27160             this.maskEl = this.el;
27161         }
27162     },
27163     
27164     initEvents : function()
27165     {
27166         this.urlAPI = (window.createObjectURL && window) || 
27167                                 (window.URL && URL.revokeObjectURL && URL) || 
27168                                 (window.webkitURL && webkitURL);
27169                         
27170         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27171         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27172         
27173         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27174         this.selectorEl.hide();
27175         
27176         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27177         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27178         
27179         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27180         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27181         this.thumbEl.hide();
27182         
27183         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27184         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27185         
27186         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27187         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27188         this.errorEl.hide();
27189         
27190         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27191         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27192         this.footerEl.hide();
27193         
27194         this.setThumbBoxSize();
27195         
27196         this.bind();
27197         
27198         this.resize();
27199         
27200         this.fireEvent('initial', this);
27201     },
27202
27203     bind : function()
27204     {
27205         var _this = this;
27206         
27207         window.addEventListener("resize", function() { _this.resize(); } );
27208         
27209         this.bodyEl.on('click', this.beforeSelectFile, this);
27210         
27211         if(Roo.isTouch){
27212             this.bodyEl.on('touchstart', this.onTouchStart, this);
27213             this.bodyEl.on('touchmove', this.onTouchMove, this);
27214             this.bodyEl.on('touchend', this.onTouchEnd, this);
27215         }
27216         
27217         if(!Roo.isTouch){
27218             this.bodyEl.on('mousedown', this.onMouseDown, this);
27219             this.bodyEl.on('mousemove', this.onMouseMove, this);
27220             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27221             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27222             Roo.get(document).on('mouseup', this.onMouseUp, this);
27223         }
27224         
27225         this.selectorEl.on('change', this.onFileSelected, this);
27226     },
27227     
27228     reset : function()
27229     {    
27230         this.scale = 0;
27231         this.baseScale = 1;
27232         this.rotate = 0;
27233         this.baseRotate = 1;
27234         this.dragable = false;
27235         this.pinching = false;
27236         this.mouseX = 0;
27237         this.mouseY = 0;
27238         this.cropData = false;
27239         this.notifyEl.dom.innerHTML = this.emptyText;
27240         
27241         this.selectorEl.dom.value = '';
27242         
27243     },
27244     
27245     resize : function()
27246     {
27247         if(this.fireEvent('resize', this) != false){
27248             this.setThumbBoxPosition();
27249             this.setCanvasPosition();
27250         }
27251     },
27252     
27253     onFooterButtonClick : function(e, el, o, type)
27254     {
27255         switch (type) {
27256             case 'rotate-left' :
27257                 this.onRotateLeft(e);
27258                 break;
27259             case 'rotate-right' :
27260                 this.onRotateRight(e);
27261                 break;
27262             case 'picture' :
27263                 this.beforeSelectFile(e);
27264                 break;
27265             case 'trash' :
27266                 this.trash(e);
27267                 break;
27268             case 'crop' :
27269                 this.crop(e);
27270                 break;
27271             case 'download' :
27272                 this.download(e);
27273                 break;
27274             default :
27275                 break;
27276         }
27277         
27278         this.fireEvent('footerbuttonclick', this, type);
27279     },
27280     
27281     beforeSelectFile : function(e)
27282     {
27283         e.preventDefault();
27284         
27285         if(this.fireEvent('beforeselectfile', this) != false){
27286             this.selectorEl.dom.click();
27287         }
27288     },
27289     
27290     onFileSelected : function(e)
27291     {
27292         e.preventDefault();
27293         
27294         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27295             return;
27296         }
27297         
27298         var file = this.selectorEl.dom.files[0];
27299         
27300         if(this.fireEvent('inspect', this, file) != false){
27301             this.prepare(file);
27302         }
27303         
27304     },
27305     
27306     trash : function(e)
27307     {
27308         this.fireEvent('trash', this);
27309     },
27310     
27311     download : function(e)
27312     {
27313         this.fireEvent('download', this);
27314     },
27315     
27316     loadCanvas : function(src)
27317     {   
27318         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27319             
27320             this.reset();
27321             
27322             this.imageEl = document.createElement('img');
27323             
27324             var _this = this;
27325             
27326             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27327             
27328             this.imageEl.src = src;
27329         }
27330     },
27331     
27332     onLoadCanvas : function()
27333     {   
27334         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27335         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27336         
27337         this.bodyEl.un('click', this.beforeSelectFile, this);
27338         
27339         this.notifyEl.hide();
27340         this.thumbEl.show();
27341         this.footerEl.show();
27342         
27343         this.baseRotateLevel();
27344         
27345         if(this.isDocument){
27346             this.setThumbBoxSize();
27347         }
27348         
27349         this.setThumbBoxPosition();
27350         
27351         this.baseScaleLevel();
27352         
27353         this.draw();
27354         
27355         this.resize();
27356         
27357         this.canvasLoaded = true;
27358         
27359         if(this.loadMask){
27360             this.maskEl.unmask();
27361         }
27362         
27363     },
27364     
27365     setCanvasPosition : function()
27366     {   
27367         if(!this.canvasEl){
27368             return;
27369         }
27370         
27371         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27372         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27373         
27374         this.previewEl.setLeft(pw);
27375         this.previewEl.setTop(ph);
27376         
27377     },
27378     
27379     onMouseDown : function(e)
27380     {   
27381         e.stopEvent();
27382         
27383         this.dragable = true;
27384         this.pinching = false;
27385         
27386         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27387             this.dragable = false;
27388             return;
27389         }
27390         
27391         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27392         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27393         
27394     },
27395     
27396     onMouseMove : function(e)
27397     {   
27398         e.stopEvent();
27399         
27400         if(!this.canvasLoaded){
27401             return;
27402         }
27403         
27404         if (!this.dragable){
27405             return;
27406         }
27407         
27408         var minX = Math.ceil(this.thumbEl.getLeft(true));
27409         var minY = Math.ceil(this.thumbEl.getTop(true));
27410         
27411         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27412         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27413         
27414         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27415         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27416         
27417         x = x - this.mouseX;
27418         y = y - this.mouseY;
27419         
27420         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27421         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27422         
27423         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27424         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27425         
27426         this.previewEl.setLeft(bgX);
27427         this.previewEl.setTop(bgY);
27428         
27429         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27430         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27431     },
27432     
27433     onMouseUp : function(e)
27434     {   
27435         e.stopEvent();
27436         
27437         this.dragable = false;
27438     },
27439     
27440     onMouseWheel : function(e)
27441     {   
27442         e.stopEvent();
27443         
27444         this.startScale = this.scale;
27445         
27446         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27447         
27448         if(!this.zoomable()){
27449             this.scale = this.startScale;
27450             return;
27451         }
27452         
27453         this.draw();
27454         
27455         return;
27456     },
27457     
27458     zoomable : function()
27459     {
27460         var minScale = this.thumbEl.getWidth() / this.minWidth;
27461         
27462         if(this.minWidth < this.minHeight){
27463             minScale = this.thumbEl.getHeight() / this.minHeight;
27464         }
27465         
27466         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27467         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27468         
27469         if(
27470                 this.isDocument &&
27471                 (this.rotate == 0 || this.rotate == 180) && 
27472                 (
27473                     width > this.imageEl.OriginWidth || 
27474                     height > this.imageEl.OriginHeight ||
27475                     (width < this.minWidth && height < this.minHeight)
27476                 )
27477         ){
27478             return false;
27479         }
27480         
27481         if(
27482                 this.isDocument &&
27483                 (this.rotate == 90 || this.rotate == 270) && 
27484                 (
27485                     width > this.imageEl.OriginWidth || 
27486                     height > this.imageEl.OriginHeight ||
27487                     (width < this.minHeight && height < this.minWidth)
27488                 )
27489         ){
27490             return false;
27491         }
27492         
27493         if(
27494                 !this.isDocument &&
27495                 (this.rotate == 0 || this.rotate == 180) && 
27496                 (
27497                     width < this.minWidth || 
27498                     width > this.imageEl.OriginWidth || 
27499                     height < this.minHeight || 
27500                     height > this.imageEl.OriginHeight
27501                 )
27502         ){
27503             return false;
27504         }
27505         
27506         if(
27507                 !this.isDocument &&
27508                 (this.rotate == 90 || this.rotate == 270) && 
27509                 (
27510                     width < this.minHeight || 
27511                     width > this.imageEl.OriginWidth || 
27512                     height < this.minWidth || 
27513                     height > this.imageEl.OriginHeight
27514                 )
27515         ){
27516             return false;
27517         }
27518         
27519         return true;
27520         
27521     },
27522     
27523     onRotateLeft : function(e)
27524     {   
27525         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27526             
27527             var minScale = this.thumbEl.getWidth() / this.minWidth;
27528             
27529             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27530             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27531             
27532             this.startScale = this.scale;
27533             
27534             while (this.getScaleLevel() < minScale){
27535             
27536                 this.scale = this.scale + 1;
27537                 
27538                 if(!this.zoomable()){
27539                     break;
27540                 }
27541                 
27542                 if(
27543                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27544                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27545                 ){
27546                     continue;
27547                 }
27548                 
27549                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27550
27551                 this.draw();
27552                 
27553                 return;
27554             }
27555             
27556             this.scale = this.startScale;
27557             
27558             this.onRotateFail();
27559             
27560             return false;
27561         }
27562         
27563         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27564
27565         if(this.isDocument){
27566             this.setThumbBoxSize();
27567             this.setThumbBoxPosition();
27568             this.setCanvasPosition();
27569         }
27570         
27571         this.draw();
27572         
27573         this.fireEvent('rotate', this, 'left');
27574         
27575     },
27576     
27577     onRotateRight : function(e)
27578     {
27579         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27580             
27581             var minScale = this.thumbEl.getWidth() / this.minWidth;
27582         
27583             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27584             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27585             
27586             this.startScale = this.scale;
27587             
27588             while (this.getScaleLevel() < minScale){
27589             
27590                 this.scale = this.scale + 1;
27591                 
27592                 if(!this.zoomable()){
27593                     break;
27594                 }
27595                 
27596                 if(
27597                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27598                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27599                 ){
27600                     continue;
27601                 }
27602                 
27603                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27604
27605                 this.draw();
27606                 
27607                 return;
27608             }
27609             
27610             this.scale = this.startScale;
27611             
27612             this.onRotateFail();
27613             
27614             return false;
27615         }
27616         
27617         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27618
27619         if(this.isDocument){
27620             this.setThumbBoxSize();
27621             this.setThumbBoxPosition();
27622             this.setCanvasPosition();
27623         }
27624         
27625         this.draw();
27626         
27627         this.fireEvent('rotate', this, 'right');
27628     },
27629     
27630     onRotateFail : function()
27631     {
27632         this.errorEl.show(true);
27633         
27634         var _this = this;
27635         
27636         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27637     },
27638     
27639     draw : function()
27640     {
27641         this.previewEl.dom.innerHTML = '';
27642         
27643         var canvasEl = document.createElement("canvas");
27644         
27645         var contextEl = canvasEl.getContext("2d");
27646         
27647         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27648         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27649         var center = this.imageEl.OriginWidth / 2;
27650         
27651         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27652             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27653             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27654             center = this.imageEl.OriginHeight / 2;
27655         }
27656         
27657         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27658         
27659         contextEl.translate(center, center);
27660         contextEl.rotate(this.rotate * Math.PI / 180);
27661
27662         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27663         
27664         this.canvasEl = document.createElement("canvas");
27665         
27666         this.contextEl = this.canvasEl.getContext("2d");
27667         
27668         switch (this.rotate) {
27669             case 0 :
27670                 
27671                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27672                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27673                 
27674                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27675                 
27676                 break;
27677             case 90 : 
27678                 
27679                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27680                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27681                 
27682                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27683                     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);
27684                     break;
27685                 }
27686                 
27687                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27688                 
27689                 break;
27690             case 180 :
27691                 
27692                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27693                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27694                 
27695                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27696                     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);
27697                     break;
27698                 }
27699                 
27700                 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);
27701                 
27702                 break;
27703             case 270 :
27704                 
27705                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27706                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27707         
27708                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27709                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27710                     break;
27711                 }
27712                 
27713                 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);
27714                 
27715                 break;
27716             default : 
27717                 break;
27718         }
27719         
27720         this.previewEl.appendChild(this.canvasEl);
27721         
27722         this.setCanvasPosition();
27723     },
27724     
27725     crop : function()
27726     {
27727         if(!this.canvasLoaded){
27728             return;
27729         }
27730         
27731         var imageCanvas = document.createElement("canvas");
27732         
27733         var imageContext = imageCanvas.getContext("2d");
27734         
27735         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27736         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27737         
27738         var center = imageCanvas.width / 2;
27739         
27740         imageContext.translate(center, center);
27741         
27742         imageContext.rotate(this.rotate * Math.PI / 180);
27743         
27744         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27745         
27746         var canvas = document.createElement("canvas");
27747         
27748         var context = canvas.getContext("2d");
27749                 
27750         canvas.width = this.minWidth;
27751         canvas.height = this.minHeight;
27752
27753         switch (this.rotate) {
27754             case 0 :
27755                 
27756                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27757                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27758                 
27759                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27760                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27761                 
27762                 var targetWidth = this.minWidth - 2 * x;
27763                 var targetHeight = this.minHeight - 2 * y;
27764                 
27765                 var scale = 1;
27766                 
27767                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27768                     scale = targetWidth / width;
27769                 }
27770                 
27771                 if(x > 0 && y == 0){
27772                     scale = targetHeight / height;
27773                 }
27774                 
27775                 if(x > 0 && y > 0){
27776                     scale = targetWidth / width;
27777                     
27778                     if(width < height){
27779                         scale = targetHeight / height;
27780                     }
27781                 }
27782                 
27783                 context.scale(scale, scale);
27784                 
27785                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27786                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27787
27788                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27789                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27790
27791                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27792                 
27793                 break;
27794             case 90 : 
27795                 
27796                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27797                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27798                 
27799                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27800                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27801                 
27802                 var targetWidth = this.minWidth - 2 * x;
27803                 var targetHeight = this.minHeight - 2 * y;
27804                 
27805                 var scale = 1;
27806                 
27807                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27808                     scale = targetWidth / width;
27809                 }
27810                 
27811                 if(x > 0 && y == 0){
27812                     scale = targetHeight / height;
27813                 }
27814                 
27815                 if(x > 0 && y > 0){
27816                     scale = targetWidth / width;
27817                     
27818                     if(width < height){
27819                         scale = targetHeight / height;
27820                     }
27821                 }
27822                 
27823                 context.scale(scale, scale);
27824                 
27825                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27826                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27827
27828                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27829                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27830                 
27831                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27832                 
27833                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27834                 
27835                 break;
27836             case 180 :
27837                 
27838                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27839                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27840                 
27841                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27842                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27843                 
27844                 var targetWidth = this.minWidth - 2 * x;
27845                 var targetHeight = this.minHeight - 2 * y;
27846                 
27847                 var scale = 1;
27848                 
27849                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27850                     scale = targetWidth / width;
27851                 }
27852                 
27853                 if(x > 0 && y == 0){
27854                     scale = targetHeight / height;
27855                 }
27856                 
27857                 if(x > 0 && y > 0){
27858                     scale = targetWidth / width;
27859                     
27860                     if(width < height){
27861                         scale = targetHeight / height;
27862                     }
27863                 }
27864                 
27865                 context.scale(scale, scale);
27866                 
27867                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27868                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27869
27870                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27871                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27872
27873                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27874                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27875                 
27876                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27877                 
27878                 break;
27879             case 270 :
27880                 
27881                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27882                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27883                 
27884                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27885                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27886                 
27887                 var targetWidth = this.minWidth - 2 * x;
27888                 var targetHeight = this.minHeight - 2 * y;
27889                 
27890                 var scale = 1;
27891                 
27892                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27893                     scale = targetWidth / width;
27894                 }
27895                 
27896                 if(x > 0 && y == 0){
27897                     scale = targetHeight / height;
27898                 }
27899                 
27900                 if(x > 0 && y > 0){
27901                     scale = targetWidth / width;
27902                     
27903                     if(width < height){
27904                         scale = targetHeight / height;
27905                     }
27906                 }
27907                 
27908                 context.scale(scale, scale);
27909                 
27910                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27911                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27912
27913                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27914                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27915                 
27916                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27917                 
27918                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27919                 
27920                 break;
27921             default : 
27922                 break;
27923         }
27924         
27925         this.cropData = canvas.toDataURL(this.cropType);
27926         
27927         if(this.fireEvent('crop', this, this.cropData) !== false){
27928             this.process(this.file, this.cropData);
27929         }
27930         
27931         return;
27932         
27933     },
27934     
27935     setThumbBoxSize : function()
27936     {
27937         var width, height;
27938         
27939         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27940             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27941             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27942             
27943             this.minWidth = width;
27944             this.minHeight = height;
27945             
27946             if(this.rotate == 90 || this.rotate == 270){
27947                 this.minWidth = height;
27948                 this.minHeight = width;
27949             }
27950         }
27951         
27952         height = 300;
27953         width = Math.ceil(this.minWidth * height / this.minHeight);
27954         
27955         if(this.minWidth > this.minHeight){
27956             width = 300;
27957             height = Math.ceil(this.minHeight * width / this.minWidth);
27958         }
27959         
27960         this.thumbEl.setStyle({
27961             width : width + 'px',
27962             height : height + 'px'
27963         });
27964
27965         return;
27966             
27967     },
27968     
27969     setThumbBoxPosition : function()
27970     {
27971         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27972         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27973         
27974         this.thumbEl.setLeft(x);
27975         this.thumbEl.setTop(y);
27976         
27977     },
27978     
27979     baseRotateLevel : function()
27980     {
27981         this.baseRotate = 1;
27982         
27983         if(
27984                 typeof(this.exif) != 'undefined' &&
27985                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27986                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27987         ){
27988             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27989         }
27990         
27991         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27992         
27993     },
27994     
27995     baseScaleLevel : function()
27996     {
27997         var width, height;
27998         
27999         if(this.isDocument){
28000             
28001             if(this.baseRotate == 6 || this.baseRotate == 8){
28002             
28003                 height = this.thumbEl.getHeight();
28004                 this.baseScale = height / this.imageEl.OriginWidth;
28005
28006                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28007                     width = this.thumbEl.getWidth();
28008                     this.baseScale = width / this.imageEl.OriginHeight;
28009                 }
28010
28011                 return;
28012             }
28013
28014             height = this.thumbEl.getHeight();
28015             this.baseScale = height / this.imageEl.OriginHeight;
28016
28017             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28018                 width = this.thumbEl.getWidth();
28019                 this.baseScale = width / this.imageEl.OriginWidth;
28020             }
28021
28022             return;
28023         }
28024         
28025         if(this.baseRotate == 6 || this.baseRotate == 8){
28026             
28027             width = this.thumbEl.getHeight();
28028             this.baseScale = width / this.imageEl.OriginHeight;
28029             
28030             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28031                 height = this.thumbEl.getWidth();
28032                 this.baseScale = height / this.imageEl.OriginHeight;
28033             }
28034             
28035             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28036                 height = this.thumbEl.getWidth();
28037                 this.baseScale = height / this.imageEl.OriginHeight;
28038                 
28039                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28040                     width = this.thumbEl.getHeight();
28041                     this.baseScale = width / this.imageEl.OriginWidth;
28042                 }
28043             }
28044             
28045             return;
28046         }
28047         
28048         width = this.thumbEl.getWidth();
28049         this.baseScale = width / this.imageEl.OriginWidth;
28050         
28051         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28052             height = this.thumbEl.getHeight();
28053             this.baseScale = height / this.imageEl.OriginHeight;
28054         }
28055         
28056         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
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         }
28067         
28068         return;
28069     },
28070     
28071     getScaleLevel : function()
28072     {
28073         return this.baseScale * Math.pow(1.1, this.scale);
28074     },
28075     
28076     onTouchStart : function(e)
28077     {
28078         if(!this.canvasLoaded){
28079             this.beforeSelectFile(e);
28080             return;
28081         }
28082         
28083         var touches = e.browserEvent.touches;
28084         
28085         if(!touches){
28086             return;
28087         }
28088         
28089         if(touches.length == 1){
28090             this.onMouseDown(e);
28091             return;
28092         }
28093         
28094         if(touches.length != 2){
28095             return;
28096         }
28097         
28098         var coords = [];
28099         
28100         for(var i = 0, finger; finger = touches[i]; i++){
28101             coords.push(finger.pageX, finger.pageY);
28102         }
28103         
28104         var x = Math.pow(coords[0] - coords[2], 2);
28105         var y = Math.pow(coords[1] - coords[3], 2);
28106         
28107         this.startDistance = Math.sqrt(x + y);
28108         
28109         this.startScale = this.scale;
28110         
28111         this.pinching = true;
28112         this.dragable = false;
28113         
28114     },
28115     
28116     onTouchMove : function(e)
28117     {
28118         if(!this.pinching && !this.dragable){
28119             return;
28120         }
28121         
28122         var touches = e.browserEvent.touches;
28123         
28124         if(!touches){
28125             return;
28126         }
28127         
28128         if(this.dragable){
28129             this.onMouseMove(e);
28130             return;
28131         }
28132         
28133         var coords = [];
28134         
28135         for(var i = 0, finger; finger = touches[i]; i++){
28136             coords.push(finger.pageX, finger.pageY);
28137         }
28138         
28139         var x = Math.pow(coords[0] - coords[2], 2);
28140         var y = Math.pow(coords[1] - coords[3], 2);
28141         
28142         this.endDistance = Math.sqrt(x + y);
28143         
28144         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28145         
28146         if(!this.zoomable()){
28147             this.scale = this.startScale;
28148             return;
28149         }
28150         
28151         this.draw();
28152         
28153     },
28154     
28155     onTouchEnd : function(e)
28156     {
28157         this.pinching = false;
28158         this.dragable = false;
28159         
28160     },
28161     
28162     process : function(file, crop)
28163     {
28164         if(this.loadMask){
28165             this.maskEl.mask(this.loadingText);
28166         }
28167         
28168         this.xhr = new XMLHttpRequest();
28169         
28170         file.xhr = this.xhr;
28171
28172         this.xhr.open(this.method, this.url, true);
28173         
28174         var headers = {
28175             "Accept": "application/json",
28176             "Cache-Control": "no-cache",
28177             "X-Requested-With": "XMLHttpRequest"
28178         };
28179         
28180         for (var headerName in headers) {
28181             var headerValue = headers[headerName];
28182             if (headerValue) {
28183                 this.xhr.setRequestHeader(headerName, headerValue);
28184             }
28185         }
28186         
28187         var _this = this;
28188         
28189         this.xhr.onload = function()
28190         {
28191             _this.xhrOnLoad(_this.xhr);
28192         }
28193         
28194         this.xhr.onerror = function()
28195         {
28196             _this.xhrOnError(_this.xhr);
28197         }
28198         
28199         var formData = new FormData();
28200
28201         formData.append('returnHTML', 'NO');
28202         
28203         if(crop){
28204             formData.append('crop', crop);
28205         }
28206         
28207         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28208             formData.append(this.paramName, file, file.name);
28209         }
28210         
28211         if(typeof(file.filename) != 'undefined'){
28212             formData.append('filename', file.filename);
28213         }
28214         
28215         if(typeof(file.mimetype) != 'undefined'){
28216             formData.append('mimetype', file.mimetype);
28217         }
28218         
28219         if(this.fireEvent('arrange', this, formData) != false){
28220             this.xhr.send(formData);
28221         };
28222     },
28223     
28224     xhrOnLoad : function(xhr)
28225     {
28226         if(this.loadMask){
28227             this.maskEl.unmask();
28228         }
28229         
28230         if (xhr.readyState !== 4) {
28231             this.fireEvent('exception', this, xhr);
28232             return;
28233         }
28234
28235         var response = Roo.decode(xhr.responseText);
28236         
28237         if(!response.success){
28238             this.fireEvent('exception', this, xhr);
28239             return;
28240         }
28241         
28242         var response = Roo.decode(xhr.responseText);
28243         
28244         this.fireEvent('upload', this, response);
28245         
28246     },
28247     
28248     xhrOnError : function()
28249     {
28250         if(this.loadMask){
28251             this.maskEl.unmask();
28252         }
28253         
28254         Roo.log('xhr on error');
28255         
28256         var response = Roo.decode(xhr.responseText);
28257           
28258         Roo.log(response);
28259         
28260     },
28261     
28262     prepare : function(file)
28263     {   
28264         if(this.loadMask){
28265             this.maskEl.mask(this.loadingText);
28266         }
28267         
28268         this.file = false;
28269         this.exif = {};
28270         
28271         if(typeof(file) === 'string'){
28272             this.loadCanvas(file);
28273             return;
28274         }
28275         
28276         if(!file || !this.urlAPI){
28277             return;
28278         }
28279         
28280         this.file = file;
28281         this.cropType = file.type;
28282         
28283         var _this = this;
28284         
28285         if(this.fireEvent('prepare', this, this.file) != false){
28286             
28287             var reader = new FileReader();
28288             
28289             reader.onload = function (e) {
28290                 if (e.target.error) {
28291                     Roo.log(e.target.error);
28292                     return;
28293                 }
28294                 
28295                 var buffer = e.target.result,
28296                     dataView = new DataView(buffer),
28297                     offset = 2,
28298                     maxOffset = dataView.byteLength - 4,
28299                     markerBytes,
28300                     markerLength;
28301                 
28302                 if (dataView.getUint16(0) === 0xffd8) {
28303                     while (offset < maxOffset) {
28304                         markerBytes = dataView.getUint16(offset);
28305                         
28306                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28307                             markerLength = dataView.getUint16(offset + 2) + 2;
28308                             if (offset + markerLength > dataView.byteLength) {
28309                                 Roo.log('Invalid meta data: Invalid segment size.');
28310                                 break;
28311                             }
28312                             
28313                             if(markerBytes == 0xffe1){
28314                                 _this.parseExifData(
28315                                     dataView,
28316                                     offset,
28317                                     markerLength
28318                                 );
28319                             }
28320                             
28321                             offset += markerLength;
28322                             
28323                             continue;
28324                         }
28325                         
28326                         break;
28327                     }
28328                     
28329                 }
28330                 
28331                 var url = _this.urlAPI.createObjectURL(_this.file);
28332                 
28333                 _this.loadCanvas(url);
28334                 
28335                 return;
28336             }
28337             
28338             reader.readAsArrayBuffer(this.file);
28339             
28340         }
28341         
28342     },
28343     
28344     parseExifData : function(dataView, offset, length)
28345     {
28346         var tiffOffset = offset + 10,
28347             littleEndian,
28348             dirOffset;
28349     
28350         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28351             // No Exif data, might be XMP data instead
28352             return;
28353         }
28354         
28355         // Check for the ASCII code for "Exif" (0x45786966):
28356         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28357             // No Exif data, might be XMP data instead
28358             return;
28359         }
28360         if (tiffOffset + 8 > dataView.byteLength) {
28361             Roo.log('Invalid Exif data: Invalid segment size.');
28362             return;
28363         }
28364         // Check for the two null bytes:
28365         if (dataView.getUint16(offset + 8) !== 0x0000) {
28366             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28367             return;
28368         }
28369         // Check the byte alignment:
28370         switch (dataView.getUint16(tiffOffset)) {
28371         case 0x4949:
28372             littleEndian = true;
28373             break;
28374         case 0x4D4D:
28375             littleEndian = false;
28376             break;
28377         default:
28378             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28379             return;
28380         }
28381         // Check for the TIFF tag marker (0x002A):
28382         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28383             Roo.log('Invalid Exif data: Missing TIFF marker.');
28384             return;
28385         }
28386         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28387         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28388         
28389         this.parseExifTags(
28390             dataView,
28391             tiffOffset,
28392             tiffOffset + dirOffset,
28393             littleEndian
28394         );
28395     },
28396     
28397     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28398     {
28399         var tagsNumber,
28400             dirEndOffset,
28401             i;
28402         if (dirOffset + 6 > dataView.byteLength) {
28403             Roo.log('Invalid Exif data: Invalid directory offset.');
28404             return;
28405         }
28406         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28407         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28408         if (dirEndOffset + 4 > dataView.byteLength) {
28409             Roo.log('Invalid Exif data: Invalid directory size.');
28410             return;
28411         }
28412         for (i = 0; i < tagsNumber; i += 1) {
28413             this.parseExifTag(
28414                 dataView,
28415                 tiffOffset,
28416                 dirOffset + 2 + 12 * i, // tag offset
28417                 littleEndian
28418             );
28419         }
28420         // Return the offset to the next directory:
28421         return dataView.getUint32(dirEndOffset, littleEndian);
28422     },
28423     
28424     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28425     {
28426         var tag = dataView.getUint16(offset, littleEndian);
28427         
28428         this.exif[tag] = this.getExifValue(
28429             dataView,
28430             tiffOffset,
28431             offset,
28432             dataView.getUint16(offset + 2, littleEndian), // tag type
28433             dataView.getUint32(offset + 4, littleEndian), // tag length
28434             littleEndian
28435         );
28436     },
28437     
28438     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28439     {
28440         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28441             tagSize,
28442             dataOffset,
28443             values,
28444             i,
28445             str,
28446             c;
28447     
28448         if (!tagType) {
28449             Roo.log('Invalid Exif data: Invalid tag type.');
28450             return;
28451         }
28452         
28453         tagSize = tagType.size * length;
28454         // Determine if the value is contained in the dataOffset bytes,
28455         // or if the value at the dataOffset is a pointer to the actual data:
28456         dataOffset = tagSize > 4 ?
28457                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28458         if (dataOffset + tagSize > dataView.byteLength) {
28459             Roo.log('Invalid Exif data: Invalid data offset.');
28460             return;
28461         }
28462         if (length === 1) {
28463             return tagType.getValue(dataView, dataOffset, littleEndian);
28464         }
28465         values = [];
28466         for (i = 0; i < length; i += 1) {
28467             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28468         }
28469         
28470         if (tagType.ascii) {
28471             str = '';
28472             // Concatenate the chars:
28473             for (i = 0; i < values.length; i += 1) {
28474                 c = values[i];
28475                 // Ignore the terminating NULL byte(s):
28476                 if (c === '\u0000') {
28477                     break;
28478                 }
28479                 str += c;
28480             }
28481             return str;
28482         }
28483         return values;
28484     }
28485     
28486 });
28487
28488 Roo.apply(Roo.bootstrap.UploadCropbox, {
28489     tags : {
28490         'Orientation': 0x0112
28491     },
28492     
28493     Orientation: {
28494             1: 0, //'top-left',
28495 //            2: 'top-right',
28496             3: 180, //'bottom-right',
28497 //            4: 'bottom-left',
28498 //            5: 'left-top',
28499             6: 90, //'right-top',
28500 //            7: 'right-bottom',
28501             8: 270 //'left-bottom'
28502     },
28503     
28504     exifTagTypes : {
28505         // byte, 8-bit unsigned int:
28506         1: {
28507             getValue: function (dataView, dataOffset) {
28508                 return dataView.getUint8(dataOffset);
28509             },
28510             size: 1
28511         },
28512         // ascii, 8-bit byte:
28513         2: {
28514             getValue: function (dataView, dataOffset) {
28515                 return String.fromCharCode(dataView.getUint8(dataOffset));
28516             },
28517             size: 1,
28518             ascii: true
28519         },
28520         // short, 16 bit int:
28521         3: {
28522             getValue: function (dataView, dataOffset, littleEndian) {
28523                 return dataView.getUint16(dataOffset, littleEndian);
28524             },
28525             size: 2
28526         },
28527         // long, 32 bit int:
28528         4: {
28529             getValue: function (dataView, dataOffset, littleEndian) {
28530                 return dataView.getUint32(dataOffset, littleEndian);
28531             },
28532             size: 4
28533         },
28534         // rational = two long values, first is numerator, second is denominator:
28535         5: {
28536             getValue: function (dataView, dataOffset, littleEndian) {
28537                 return dataView.getUint32(dataOffset, littleEndian) /
28538                     dataView.getUint32(dataOffset + 4, littleEndian);
28539             },
28540             size: 8
28541         },
28542         // slong, 32 bit signed int:
28543         9: {
28544             getValue: function (dataView, dataOffset, littleEndian) {
28545                 return dataView.getInt32(dataOffset, littleEndian);
28546             },
28547             size: 4
28548         },
28549         // srational, two slongs, first is numerator, second is denominator:
28550         10: {
28551             getValue: function (dataView, dataOffset, littleEndian) {
28552                 return dataView.getInt32(dataOffset, littleEndian) /
28553                     dataView.getInt32(dataOffset + 4, littleEndian);
28554             },
28555             size: 8
28556         }
28557     },
28558     
28559     footer : {
28560         STANDARD : [
28561             {
28562                 tag : 'div',
28563                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28564                 action : 'rotate-left',
28565                 cn : [
28566                     {
28567                         tag : 'button',
28568                         cls : 'btn btn-default',
28569                         html : '<i class="fa fa-undo"></i>'
28570                     }
28571                 ]
28572             },
28573             {
28574                 tag : 'div',
28575                 cls : 'btn-group roo-upload-cropbox-picture',
28576                 action : 'picture',
28577                 cn : [
28578                     {
28579                         tag : 'button',
28580                         cls : 'btn btn-default',
28581                         html : '<i class="fa fa-picture-o"></i>'
28582                     }
28583                 ]
28584             },
28585             {
28586                 tag : 'div',
28587                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28588                 action : 'rotate-right',
28589                 cn : [
28590                     {
28591                         tag : 'button',
28592                         cls : 'btn btn-default',
28593                         html : '<i class="fa fa-repeat"></i>'
28594                     }
28595                 ]
28596             }
28597         ],
28598         DOCUMENT : [
28599             {
28600                 tag : 'div',
28601                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28602                 action : 'rotate-left',
28603                 cn : [
28604                     {
28605                         tag : 'button',
28606                         cls : 'btn btn-default',
28607                         html : '<i class="fa fa-undo"></i>'
28608                     }
28609                 ]
28610             },
28611             {
28612                 tag : 'div',
28613                 cls : 'btn-group roo-upload-cropbox-download',
28614                 action : 'download',
28615                 cn : [
28616                     {
28617                         tag : 'button',
28618                         cls : 'btn btn-default',
28619                         html : '<i class="fa fa-download"></i>'
28620                     }
28621                 ]
28622             },
28623             {
28624                 tag : 'div',
28625                 cls : 'btn-group roo-upload-cropbox-crop',
28626                 action : 'crop',
28627                 cn : [
28628                     {
28629                         tag : 'button',
28630                         cls : 'btn btn-default',
28631                         html : '<i class="fa fa-crop"></i>'
28632                     }
28633                 ]
28634             },
28635             {
28636                 tag : 'div',
28637                 cls : 'btn-group roo-upload-cropbox-trash',
28638                 action : 'trash',
28639                 cn : [
28640                     {
28641                         tag : 'button',
28642                         cls : 'btn btn-default',
28643                         html : '<i class="fa fa-trash"></i>'
28644                     }
28645                 ]
28646             },
28647             {
28648                 tag : 'div',
28649                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28650                 action : 'rotate-right',
28651                 cn : [
28652                     {
28653                         tag : 'button',
28654                         cls : 'btn btn-default',
28655                         html : '<i class="fa fa-repeat"></i>'
28656                     }
28657                 ]
28658             }
28659         ],
28660         ROTATOR : [
28661             {
28662                 tag : 'div',
28663                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28664                 action : 'rotate-left',
28665                 cn : [
28666                     {
28667                         tag : 'button',
28668                         cls : 'btn btn-default',
28669                         html : '<i class="fa fa-undo"></i>'
28670                     }
28671                 ]
28672             },
28673             {
28674                 tag : 'div',
28675                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28676                 action : 'rotate-right',
28677                 cn : [
28678                     {
28679                         tag : 'button',
28680                         cls : 'btn btn-default',
28681                         html : '<i class="fa fa-repeat"></i>'
28682                     }
28683                 ]
28684             }
28685         ]
28686     }
28687 });
28688
28689 /*
28690 * Licence: LGPL
28691 */
28692
28693 /**
28694  * @class Roo.bootstrap.DocumentManager
28695  * @extends Roo.bootstrap.Component
28696  * Bootstrap DocumentManager class
28697  * @cfg {String} paramName default 'imageUpload'
28698  * @cfg {String} toolTipName default 'filename'
28699  * @cfg {String} method default POST
28700  * @cfg {String} url action url
28701  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28702  * @cfg {Boolean} multiple multiple upload default true
28703  * @cfg {Number} thumbSize default 300
28704  * @cfg {String} fieldLabel
28705  * @cfg {Number} labelWidth default 4
28706  * @cfg {String} labelAlign (left|top) default left
28707  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28708 * @cfg {Number} labellg set the width of label (1-12)
28709  * @cfg {Number} labelmd set the width of label (1-12)
28710  * @cfg {Number} labelsm set the width of label (1-12)
28711  * @cfg {Number} labelxs set the width of label (1-12)
28712  * 
28713  * @constructor
28714  * Create a new DocumentManager
28715  * @param {Object} config The config object
28716  */
28717
28718 Roo.bootstrap.DocumentManager = function(config){
28719     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28720     
28721     this.files = [];
28722     this.delegates = [];
28723     
28724     this.addEvents({
28725         /**
28726          * @event initial
28727          * Fire when initial the DocumentManager
28728          * @param {Roo.bootstrap.DocumentManager} this
28729          */
28730         "initial" : true,
28731         /**
28732          * @event inspect
28733          * inspect selected file
28734          * @param {Roo.bootstrap.DocumentManager} this
28735          * @param {File} file
28736          */
28737         "inspect" : true,
28738         /**
28739          * @event exception
28740          * Fire when xhr load exception
28741          * @param {Roo.bootstrap.DocumentManager} this
28742          * @param {XMLHttpRequest} xhr
28743          */
28744         "exception" : true,
28745         /**
28746          * @event afterupload
28747          * Fire when xhr load exception
28748          * @param {Roo.bootstrap.DocumentManager} this
28749          * @param {XMLHttpRequest} xhr
28750          */
28751         "afterupload" : true,
28752         /**
28753          * @event prepare
28754          * prepare the form data
28755          * @param {Roo.bootstrap.DocumentManager} this
28756          * @param {Object} formData
28757          */
28758         "prepare" : true,
28759         /**
28760          * @event remove
28761          * Fire when remove the file
28762          * @param {Roo.bootstrap.DocumentManager} this
28763          * @param {Object} file
28764          */
28765         "remove" : true,
28766         /**
28767          * @event refresh
28768          * Fire after refresh the file
28769          * @param {Roo.bootstrap.DocumentManager} this
28770          */
28771         "refresh" : true,
28772         /**
28773          * @event click
28774          * Fire after click the image
28775          * @param {Roo.bootstrap.DocumentManager} this
28776          * @param {Object} file
28777          */
28778         "click" : true,
28779         /**
28780          * @event edit
28781          * Fire when upload a image and editable set to true
28782          * @param {Roo.bootstrap.DocumentManager} this
28783          * @param {Object} file
28784          */
28785         "edit" : true,
28786         /**
28787          * @event beforeselectfile
28788          * Fire before select file
28789          * @param {Roo.bootstrap.DocumentManager} this
28790          */
28791         "beforeselectfile" : true,
28792         /**
28793          * @event process
28794          * Fire before process file
28795          * @param {Roo.bootstrap.DocumentManager} this
28796          * @param {Object} file
28797          */
28798         "process" : true,
28799         /**
28800          * @event previewrendered
28801          * Fire when preview rendered
28802          * @param {Roo.bootstrap.DocumentManager} this
28803          * @param {Object} file
28804          */
28805         "previewrendered" : true,
28806         /**
28807          */
28808         "previewResize" : true
28809         
28810     });
28811 };
28812
28813 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28814     
28815     boxes : 0,
28816     inputName : '',
28817     thumbSize : 300,
28818     multiple : true,
28819     files : false,
28820     method : 'POST',
28821     url : '',
28822     paramName : 'imageUpload',
28823     toolTipName : 'filename',
28824     fieldLabel : '',
28825     labelWidth : 4,
28826     labelAlign : 'left',
28827     editable : true,
28828     delegates : false,
28829     xhr : false, 
28830     
28831     labellg : 0,
28832     labelmd : 0,
28833     labelsm : 0,
28834     labelxs : 0,
28835     
28836     getAutoCreate : function()
28837     {   
28838         var managerWidget = {
28839             tag : 'div',
28840             cls : 'roo-document-manager',
28841             cn : [
28842                 {
28843                     tag : 'input',
28844                     cls : 'roo-document-manager-selector',
28845                     type : 'file'
28846                 },
28847                 {
28848                     tag : 'div',
28849                     cls : 'roo-document-manager-uploader',
28850                     cn : [
28851                         {
28852                             tag : 'div',
28853                             cls : 'roo-document-manager-upload-btn',
28854                             html : '<i class="fa fa-plus"></i>'
28855                         }
28856                     ]
28857                     
28858                 }
28859             ]
28860         };
28861         
28862         var content = [
28863             {
28864                 tag : 'div',
28865                 cls : 'column col-md-12',
28866                 cn : managerWidget
28867             }
28868         ];
28869         
28870         if(this.fieldLabel.length){
28871             
28872             content = [
28873                 {
28874                     tag : 'div',
28875                     cls : 'column col-md-12',
28876                     html : this.fieldLabel
28877                 },
28878                 {
28879                     tag : 'div',
28880                     cls : 'column col-md-12',
28881                     cn : managerWidget
28882                 }
28883             ];
28884
28885             if(this.labelAlign == 'left'){
28886                 content = [
28887                     {
28888                         tag : 'div',
28889                         cls : 'column',
28890                         html : this.fieldLabel
28891                     },
28892                     {
28893                         tag : 'div',
28894                         cls : 'column',
28895                         cn : managerWidget
28896                     }
28897                 ];
28898                 
28899                 if(this.labelWidth > 12){
28900                     content[0].style = "width: " + this.labelWidth + 'px';
28901                 }
28902
28903                 if(this.labelWidth < 13 && this.labelmd == 0){
28904                     this.labelmd = this.labelWidth;
28905                 }
28906
28907                 if(this.labellg > 0){
28908                     content[0].cls += ' col-lg-' + this.labellg;
28909                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28910                 }
28911
28912                 if(this.labelmd > 0){
28913                     content[0].cls += ' col-md-' + this.labelmd;
28914                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28915                 }
28916
28917                 if(this.labelsm > 0){
28918                     content[0].cls += ' col-sm-' + this.labelsm;
28919                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28920                 }
28921
28922                 if(this.labelxs > 0){
28923                     content[0].cls += ' col-xs-' + this.labelxs;
28924                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28925                 }
28926                 
28927             }
28928         }
28929         
28930         var cfg = {
28931             tag : 'div',
28932             cls : 'row clearfix',
28933             cn : content
28934         };
28935         
28936         return cfg;
28937         
28938     },
28939     
28940     initEvents : function()
28941     {
28942         this.managerEl = this.el.select('.roo-document-manager', true).first();
28943         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28944         
28945         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28946         this.selectorEl.hide();
28947         
28948         if(this.multiple){
28949             this.selectorEl.attr('multiple', 'multiple');
28950         }
28951         
28952         this.selectorEl.on('change', this.onFileSelected, this);
28953         
28954         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28955         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28956         
28957         this.uploader.on('click', this.onUploaderClick, this);
28958         
28959         this.renderProgressDialog();
28960         
28961         var _this = this;
28962         
28963         window.addEventListener("resize", function() { _this.refresh(); } );
28964         
28965         this.fireEvent('initial', this);
28966     },
28967     
28968     renderProgressDialog : function()
28969     {
28970         var _this = this;
28971         
28972         this.progressDialog = new Roo.bootstrap.Modal({
28973             cls : 'roo-document-manager-progress-dialog',
28974             allow_close : false,
28975             title : '',
28976             buttons : [
28977                 {
28978                     name  :'cancel',
28979                     weight : 'danger',
28980                     html : 'Cancel'
28981                 }
28982             ], 
28983             listeners : { 
28984                 btnclick : function() {
28985                     _this.uploadCancel();
28986                     this.hide();
28987                 }
28988             }
28989         });
28990          
28991         this.progressDialog.render(Roo.get(document.body));
28992          
28993         this.progress = new Roo.bootstrap.Progress({
28994             cls : 'roo-document-manager-progress',
28995             active : true,
28996             striped : true
28997         });
28998         
28999         this.progress.render(this.progressDialog.getChildContainer());
29000         
29001         this.progressBar = new Roo.bootstrap.ProgressBar({
29002             cls : 'roo-document-manager-progress-bar',
29003             aria_valuenow : 0,
29004             aria_valuemin : 0,
29005             aria_valuemax : 12,
29006             panel : 'success'
29007         });
29008         
29009         this.progressBar.render(this.progress.getChildContainer());
29010     },
29011     
29012     onUploaderClick : function(e)
29013     {
29014         e.preventDefault();
29015      
29016         if(this.fireEvent('beforeselectfile', this) != false){
29017             this.selectorEl.dom.click();
29018         }
29019         
29020     },
29021     
29022     onFileSelected : function(e)
29023     {
29024         e.preventDefault();
29025         
29026         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29027             return;
29028         }
29029         
29030         Roo.each(this.selectorEl.dom.files, function(file){
29031             if(this.fireEvent('inspect', this, file) != false){
29032                 this.files.push(file);
29033             }
29034         }, this);
29035         
29036         this.queue();
29037         
29038     },
29039     
29040     queue : function()
29041     {
29042         this.selectorEl.dom.value = '';
29043         
29044         if(!this.files || !this.files.length){
29045             return;
29046         }
29047         
29048         if(this.boxes > 0 && this.files.length > this.boxes){
29049             this.files = this.files.slice(0, this.boxes);
29050         }
29051         
29052         this.uploader.show();
29053         
29054         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29055             this.uploader.hide();
29056         }
29057         
29058         var _this = this;
29059         
29060         var files = [];
29061         
29062         var docs = [];
29063         
29064         Roo.each(this.files, function(file){
29065             
29066             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29067                 var f = this.renderPreview(file);
29068                 files.push(f);
29069                 return;
29070             }
29071             
29072             if(file.type.indexOf('image') != -1){
29073                 this.delegates.push(
29074                     (function(){
29075                         _this.process(file);
29076                     }).createDelegate(this)
29077                 );
29078         
29079                 return;
29080             }
29081             
29082             docs.push(
29083                 (function(){
29084                     _this.process(file);
29085                 }).createDelegate(this)
29086             );
29087             
29088         }, this);
29089         
29090         this.files = files;
29091         
29092         this.delegates = this.delegates.concat(docs);
29093         
29094         if(!this.delegates.length){
29095             this.refresh();
29096             return;
29097         }
29098         
29099         this.progressBar.aria_valuemax = this.delegates.length;
29100         
29101         this.arrange();
29102         
29103         return;
29104     },
29105     
29106     arrange : function()
29107     {
29108         if(!this.delegates.length){
29109             this.progressDialog.hide();
29110             this.refresh();
29111             return;
29112         }
29113         
29114         var delegate = this.delegates.shift();
29115         
29116         this.progressDialog.show();
29117         
29118         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29119         
29120         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29121         
29122         delegate();
29123     },
29124     
29125     refresh : function()
29126     {
29127         this.uploader.show();
29128         
29129         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29130             this.uploader.hide();
29131         }
29132         
29133         Roo.isTouch ? this.closable(false) : this.closable(true);
29134         
29135         this.fireEvent('refresh', this);
29136     },
29137     
29138     onRemove : function(e, el, o)
29139     {
29140         e.preventDefault();
29141         
29142         this.fireEvent('remove', this, o);
29143         
29144     },
29145     
29146     remove : function(o)
29147     {
29148         var files = [];
29149         
29150         Roo.each(this.files, function(file){
29151             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29152                 files.push(file);
29153                 return;
29154             }
29155
29156             o.target.remove();
29157
29158         }, this);
29159         
29160         this.files = files;
29161         
29162         this.refresh();
29163     },
29164     
29165     clear : function()
29166     {
29167         Roo.each(this.files, function(file){
29168             if(!file.target){
29169                 return;
29170             }
29171             
29172             file.target.remove();
29173
29174         }, this);
29175         
29176         this.files = [];
29177         
29178         this.refresh();
29179     },
29180     
29181     onClick : function(e, el, o)
29182     {
29183         e.preventDefault();
29184         
29185         this.fireEvent('click', this, o);
29186         
29187     },
29188     
29189     closable : function(closable)
29190     {
29191         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29192             
29193             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29194             
29195             if(closable){
29196                 el.show();
29197                 return;
29198             }
29199             
29200             el.hide();
29201             
29202         }, this);
29203     },
29204     
29205     xhrOnLoad : function(xhr)
29206     {
29207         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29208             el.remove();
29209         }, this);
29210         
29211         if (xhr.readyState !== 4) {
29212             this.arrange();
29213             this.fireEvent('exception', this, xhr);
29214             return;
29215         }
29216
29217         var response = Roo.decode(xhr.responseText);
29218         
29219         if(!response.success){
29220             this.arrange();
29221             this.fireEvent('exception', this, xhr);
29222             return;
29223         }
29224         
29225         var file = this.renderPreview(response.data);
29226         
29227         this.files.push(file);
29228         
29229         this.arrange();
29230         
29231         this.fireEvent('afterupload', this, xhr);
29232         
29233     },
29234     
29235     xhrOnError : function(xhr)
29236     {
29237         Roo.log('xhr on error');
29238         
29239         var response = Roo.decode(xhr.responseText);
29240           
29241         Roo.log(response);
29242         
29243         this.arrange();
29244     },
29245     
29246     process : function(file)
29247     {
29248         if(this.fireEvent('process', this, file) !== false){
29249             if(this.editable && file.type.indexOf('image') != -1){
29250                 this.fireEvent('edit', this, file);
29251                 return;
29252             }
29253
29254             this.uploadStart(file, false);
29255
29256             return;
29257         }
29258         
29259     },
29260     
29261     uploadStart : function(file, crop)
29262     {
29263         this.xhr = new XMLHttpRequest();
29264         
29265         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29266             this.arrange();
29267             return;
29268         }
29269         
29270         file.xhr = this.xhr;
29271             
29272         this.managerEl.createChild({
29273             tag : 'div',
29274             cls : 'roo-document-manager-loading',
29275             cn : [
29276                 {
29277                     tag : 'div',
29278                     tooltip : file.name,
29279                     cls : 'roo-document-manager-thumb',
29280                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29281                 }
29282             ]
29283
29284         });
29285
29286         this.xhr.open(this.method, this.url, true);
29287         
29288         var headers = {
29289             "Accept": "application/json",
29290             "Cache-Control": "no-cache",
29291             "X-Requested-With": "XMLHttpRequest"
29292         };
29293         
29294         for (var headerName in headers) {
29295             var headerValue = headers[headerName];
29296             if (headerValue) {
29297                 this.xhr.setRequestHeader(headerName, headerValue);
29298             }
29299         }
29300         
29301         var _this = this;
29302         
29303         this.xhr.onload = function()
29304         {
29305             _this.xhrOnLoad(_this.xhr);
29306         }
29307         
29308         this.xhr.onerror = function()
29309         {
29310             _this.xhrOnError(_this.xhr);
29311         }
29312         
29313         var formData = new FormData();
29314
29315         formData.append('returnHTML', 'NO');
29316         
29317         if(crop){
29318             formData.append('crop', crop);
29319         }
29320         
29321         formData.append(this.paramName, file, file.name);
29322         
29323         var options = {
29324             file : file, 
29325             manually : false
29326         };
29327         
29328         if(this.fireEvent('prepare', this, formData, options) != false){
29329             
29330             if(options.manually){
29331                 return;
29332             }
29333             
29334             this.xhr.send(formData);
29335             return;
29336         };
29337         
29338         this.uploadCancel();
29339     },
29340     
29341     uploadCancel : function()
29342     {
29343         if (this.xhr) {
29344             this.xhr.abort();
29345         }
29346         
29347         this.delegates = [];
29348         
29349         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29350             el.remove();
29351         }, this);
29352         
29353         this.arrange();
29354     },
29355     
29356     renderPreview : function(file)
29357     {
29358         if(typeof(file.target) != 'undefined' && file.target){
29359             return file;
29360         }
29361         
29362         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29363         
29364         var previewEl = this.managerEl.createChild({
29365             tag : 'div',
29366             cls : 'roo-document-manager-preview',
29367             cn : [
29368                 {
29369                     tag : 'div',
29370                     tooltip : file[this.toolTipName],
29371                     cls : 'roo-document-manager-thumb',
29372                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29373                 },
29374                 {
29375                     tag : 'button',
29376                     cls : 'close',
29377                     html : '<i class="fa fa-times-circle"></i>'
29378                 }
29379             ]
29380         });
29381
29382         var close = previewEl.select('button.close', true).first();
29383
29384         close.on('click', this.onRemove, this, file);
29385
29386         file.target = previewEl;
29387
29388         var image = previewEl.select('img', true).first();
29389         
29390         var _this = this;
29391         
29392         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29393         
29394         image.on('click', this.onClick, this, file);
29395         
29396         this.fireEvent('previewrendered', this, file);
29397         
29398         return file;
29399         
29400     },
29401     
29402     onPreviewLoad : function(file, image)
29403     {
29404         if(typeof(file.target) == 'undefined' || !file.target){
29405             return;
29406         }
29407         
29408         var width = image.dom.naturalWidth || image.dom.width;
29409         var height = image.dom.naturalHeight || image.dom.height;
29410         
29411         if(!this.previewResize) {
29412             return;
29413         }
29414         
29415         if(width > height){
29416             file.target.addClass('wide');
29417             return;
29418         }
29419         
29420         file.target.addClass('tall');
29421         return;
29422         
29423     },
29424     
29425     uploadFromSource : function(file, crop)
29426     {
29427         this.xhr = new XMLHttpRequest();
29428         
29429         this.managerEl.createChild({
29430             tag : 'div',
29431             cls : 'roo-document-manager-loading',
29432             cn : [
29433                 {
29434                     tag : 'div',
29435                     tooltip : file.name,
29436                     cls : 'roo-document-manager-thumb',
29437                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29438                 }
29439             ]
29440
29441         });
29442
29443         this.xhr.open(this.method, this.url, true);
29444         
29445         var headers = {
29446             "Accept": "application/json",
29447             "Cache-Control": "no-cache",
29448             "X-Requested-With": "XMLHttpRequest"
29449         };
29450         
29451         for (var headerName in headers) {
29452             var headerValue = headers[headerName];
29453             if (headerValue) {
29454                 this.xhr.setRequestHeader(headerName, headerValue);
29455             }
29456         }
29457         
29458         var _this = this;
29459         
29460         this.xhr.onload = function()
29461         {
29462             _this.xhrOnLoad(_this.xhr);
29463         }
29464         
29465         this.xhr.onerror = function()
29466         {
29467             _this.xhrOnError(_this.xhr);
29468         }
29469         
29470         var formData = new FormData();
29471
29472         formData.append('returnHTML', 'NO');
29473         
29474         formData.append('crop', crop);
29475         
29476         if(typeof(file.filename) != 'undefined'){
29477             formData.append('filename', file.filename);
29478         }
29479         
29480         if(typeof(file.mimetype) != 'undefined'){
29481             formData.append('mimetype', file.mimetype);
29482         }
29483         
29484         Roo.log(formData);
29485         
29486         if(this.fireEvent('prepare', this, formData) != false){
29487             this.xhr.send(formData);
29488         };
29489     }
29490 });
29491
29492 /*
29493 * Licence: LGPL
29494 */
29495
29496 /**
29497  * @class Roo.bootstrap.DocumentViewer
29498  * @extends Roo.bootstrap.Component
29499  * Bootstrap DocumentViewer class
29500  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29501  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29502  * 
29503  * @constructor
29504  * Create a new DocumentViewer
29505  * @param {Object} config The config object
29506  */
29507
29508 Roo.bootstrap.DocumentViewer = function(config){
29509     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29510     
29511     this.addEvents({
29512         /**
29513          * @event initial
29514          * Fire after initEvent
29515          * @param {Roo.bootstrap.DocumentViewer} this
29516          */
29517         "initial" : true,
29518         /**
29519          * @event click
29520          * Fire after click
29521          * @param {Roo.bootstrap.DocumentViewer} this
29522          */
29523         "click" : true,
29524         /**
29525          * @event download
29526          * Fire after download button
29527          * @param {Roo.bootstrap.DocumentViewer} this
29528          */
29529         "download" : true,
29530         /**
29531          * @event trash
29532          * Fire after trash button
29533          * @param {Roo.bootstrap.DocumentViewer} this
29534          */
29535         "trash" : true
29536         
29537     });
29538 };
29539
29540 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29541     
29542     showDownload : true,
29543     
29544     showTrash : true,
29545     
29546     getAutoCreate : function()
29547     {
29548         var cfg = {
29549             tag : 'div',
29550             cls : 'roo-document-viewer',
29551             cn : [
29552                 {
29553                     tag : 'div',
29554                     cls : 'roo-document-viewer-body',
29555                     cn : [
29556                         {
29557                             tag : 'div',
29558                             cls : 'roo-document-viewer-thumb',
29559                             cn : [
29560                                 {
29561                                     tag : 'img',
29562                                     cls : 'roo-document-viewer-image'
29563                                 }
29564                             ]
29565                         }
29566                     ]
29567                 },
29568                 {
29569                     tag : 'div',
29570                     cls : 'roo-document-viewer-footer',
29571                     cn : {
29572                         tag : 'div',
29573                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29574                         cn : [
29575                             {
29576                                 tag : 'div',
29577                                 cls : 'btn-group roo-document-viewer-download',
29578                                 cn : [
29579                                     {
29580                                         tag : 'button',
29581                                         cls : 'btn btn-default',
29582                                         html : '<i class="fa fa-download"></i>'
29583                                     }
29584                                 ]
29585                             },
29586                             {
29587                                 tag : 'div',
29588                                 cls : 'btn-group roo-document-viewer-trash',
29589                                 cn : [
29590                                     {
29591                                         tag : 'button',
29592                                         cls : 'btn btn-default',
29593                                         html : '<i class="fa fa-trash"></i>'
29594                                     }
29595                                 ]
29596                             }
29597                         ]
29598                     }
29599                 }
29600             ]
29601         };
29602         
29603         return cfg;
29604     },
29605     
29606     initEvents : function()
29607     {
29608         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29609         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29610         
29611         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29612         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29613         
29614         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29615         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29616         
29617         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29618         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29619         
29620         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29621         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29622         
29623         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29624         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29625         
29626         this.bodyEl.on('click', this.onClick, this);
29627         this.downloadBtn.on('click', this.onDownload, this);
29628         this.trashBtn.on('click', this.onTrash, this);
29629         
29630         this.downloadBtn.hide();
29631         this.trashBtn.hide();
29632         
29633         if(this.showDownload){
29634             this.downloadBtn.show();
29635         }
29636         
29637         if(this.showTrash){
29638             this.trashBtn.show();
29639         }
29640         
29641         if(!this.showDownload && !this.showTrash) {
29642             this.footerEl.hide();
29643         }
29644         
29645     },
29646     
29647     initial : function()
29648     {
29649         this.fireEvent('initial', this);
29650         
29651     },
29652     
29653     onClick : function(e)
29654     {
29655         e.preventDefault();
29656         
29657         this.fireEvent('click', this);
29658     },
29659     
29660     onDownload : function(e)
29661     {
29662         e.preventDefault();
29663         
29664         this.fireEvent('download', this);
29665     },
29666     
29667     onTrash : function(e)
29668     {
29669         e.preventDefault();
29670         
29671         this.fireEvent('trash', this);
29672     }
29673     
29674 });
29675 /*
29676  * - LGPL
29677  *
29678  * nav progress bar
29679  * 
29680  */
29681
29682 /**
29683  * @class Roo.bootstrap.NavProgressBar
29684  * @extends Roo.bootstrap.Component
29685  * Bootstrap NavProgressBar class
29686  * 
29687  * @constructor
29688  * Create a new nav progress bar
29689  * @param {Object} config The config object
29690  */
29691
29692 Roo.bootstrap.NavProgressBar = function(config){
29693     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29694
29695     this.bullets = this.bullets || [];
29696    
29697 //    Roo.bootstrap.NavProgressBar.register(this);
29698      this.addEvents({
29699         /**
29700              * @event changed
29701              * Fires when the active item changes
29702              * @param {Roo.bootstrap.NavProgressBar} this
29703              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29704              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29705          */
29706         'changed': true
29707      });
29708     
29709 };
29710
29711 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29712     
29713     bullets : [],
29714     barItems : [],
29715     
29716     getAutoCreate : function()
29717     {
29718         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29719         
29720         cfg = {
29721             tag : 'div',
29722             cls : 'roo-navigation-bar-group',
29723             cn : [
29724                 {
29725                     tag : 'div',
29726                     cls : 'roo-navigation-top-bar'
29727                 },
29728                 {
29729                     tag : 'div',
29730                     cls : 'roo-navigation-bullets-bar',
29731                     cn : [
29732                         {
29733                             tag : 'ul',
29734                             cls : 'roo-navigation-bar'
29735                         }
29736                     ]
29737                 },
29738                 
29739                 {
29740                     tag : 'div',
29741                     cls : 'roo-navigation-bottom-bar'
29742                 }
29743             ]
29744             
29745         };
29746         
29747         return cfg;
29748         
29749     },
29750     
29751     initEvents: function() 
29752     {
29753         
29754     },
29755     
29756     onRender : function(ct, position) 
29757     {
29758         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29759         
29760         if(this.bullets.length){
29761             Roo.each(this.bullets, function(b){
29762                this.addItem(b);
29763             }, this);
29764         }
29765         
29766         this.format();
29767         
29768     },
29769     
29770     addItem : function(cfg)
29771     {
29772         var item = new Roo.bootstrap.NavProgressItem(cfg);
29773         
29774         item.parentId = this.id;
29775         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29776         
29777         if(cfg.html){
29778             var top = new Roo.bootstrap.Element({
29779                 tag : 'div',
29780                 cls : 'roo-navigation-bar-text'
29781             });
29782             
29783             var bottom = new Roo.bootstrap.Element({
29784                 tag : 'div',
29785                 cls : 'roo-navigation-bar-text'
29786             });
29787             
29788             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29789             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29790             
29791             var topText = new Roo.bootstrap.Element({
29792                 tag : 'span',
29793                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29794             });
29795             
29796             var bottomText = new Roo.bootstrap.Element({
29797                 tag : 'span',
29798                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29799             });
29800             
29801             topText.onRender(top.el, null);
29802             bottomText.onRender(bottom.el, null);
29803             
29804             item.topEl = top;
29805             item.bottomEl = bottom;
29806         }
29807         
29808         this.barItems.push(item);
29809         
29810         return item;
29811     },
29812     
29813     getActive : function()
29814     {
29815         var active = false;
29816         
29817         Roo.each(this.barItems, function(v){
29818             
29819             if (!v.isActive()) {
29820                 return;
29821             }
29822             
29823             active = v;
29824             return false;
29825             
29826         });
29827         
29828         return active;
29829     },
29830     
29831     setActiveItem : function(item)
29832     {
29833         var prev = false;
29834         
29835         Roo.each(this.barItems, function(v){
29836             if (v.rid == item.rid) {
29837                 return ;
29838             }
29839             
29840             if (v.isActive()) {
29841                 v.setActive(false);
29842                 prev = v;
29843             }
29844         });
29845
29846         item.setActive(true);
29847         
29848         this.fireEvent('changed', this, item, prev);
29849     },
29850     
29851     getBarItem: function(rid)
29852     {
29853         var ret = false;
29854         
29855         Roo.each(this.barItems, function(e) {
29856             if (e.rid != rid) {
29857                 return;
29858             }
29859             
29860             ret =  e;
29861             return false;
29862         });
29863         
29864         return ret;
29865     },
29866     
29867     indexOfItem : function(item)
29868     {
29869         var index = false;
29870         
29871         Roo.each(this.barItems, function(v, i){
29872             
29873             if (v.rid != item.rid) {
29874                 return;
29875             }
29876             
29877             index = i;
29878             return false
29879         });
29880         
29881         return index;
29882     },
29883     
29884     setActiveNext : function()
29885     {
29886         var i = this.indexOfItem(this.getActive());
29887         
29888         if (i > this.barItems.length) {
29889             return;
29890         }
29891         
29892         this.setActiveItem(this.barItems[i+1]);
29893     },
29894     
29895     setActivePrev : function()
29896     {
29897         var i = this.indexOfItem(this.getActive());
29898         
29899         if (i  < 1) {
29900             return;
29901         }
29902         
29903         this.setActiveItem(this.barItems[i-1]);
29904     },
29905     
29906     format : function()
29907     {
29908         if(!this.barItems.length){
29909             return;
29910         }
29911      
29912         var width = 100 / this.barItems.length;
29913         
29914         Roo.each(this.barItems, function(i){
29915             i.el.setStyle('width', width + '%');
29916             i.topEl.el.setStyle('width', width + '%');
29917             i.bottomEl.el.setStyle('width', width + '%');
29918         }, this);
29919         
29920     }
29921     
29922 });
29923 /*
29924  * - LGPL
29925  *
29926  * Nav Progress Item
29927  * 
29928  */
29929
29930 /**
29931  * @class Roo.bootstrap.NavProgressItem
29932  * @extends Roo.bootstrap.Component
29933  * Bootstrap NavProgressItem class
29934  * @cfg {String} rid the reference id
29935  * @cfg {Boolean} active (true|false) Is item active default false
29936  * @cfg {Boolean} disabled (true|false) Is item active default false
29937  * @cfg {String} html
29938  * @cfg {String} position (top|bottom) text position default bottom
29939  * @cfg {String} icon show icon instead of number
29940  * 
29941  * @constructor
29942  * Create a new NavProgressItem
29943  * @param {Object} config The config object
29944  */
29945 Roo.bootstrap.NavProgressItem = function(config){
29946     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29947     this.addEvents({
29948         // raw events
29949         /**
29950          * @event click
29951          * The raw click event for the entire grid.
29952          * @param {Roo.bootstrap.NavProgressItem} this
29953          * @param {Roo.EventObject} e
29954          */
29955         "click" : true
29956     });
29957    
29958 };
29959
29960 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29961     
29962     rid : '',
29963     active : false,
29964     disabled : false,
29965     html : '',
29966     position : 'bottom',
29967     icon : false,
29968     
29969     getAutoCreate : function()
29970     {
29971         var iconCls = 'roo-navigation-bar-item-icon';
29972         
29973         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29974         
29975         var cfg = {
29976             tag: 'li',
29977             cls: 'roo-navigation-bar-item',
29978             cn : [
29979                 {
29980                     tag : 'i',
29981                     cls : iconCls
29982                 }
29983             ]
29984         };
29985         
29986         if(this.active){
29987             cfg.cls += ' active';
29988         }
29989         if(this.disabled){
29990             cfg.cls += ' disabled';
29991         }
29992         
29993         return cfg;
29994     },
29995     
29996     disable : function()
29997     {
29998         this.setDisabled(true);
29999     },
30000     
30001     enable : function()
30002     {
30003         this.setDisabled(false);
30004     },
30005     
30006     initEvents: function() 
30007     {
30008         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30009         
30010         this.iconEl.on('click', this.onClick, this);
30011     },
30012     
30013     onClick : function(e)
30014     {
30015         e.preventDefault();
30016         
30017         if(this.disabled){
30018             return;
30019         }
30020         
30021         if(this.fireEvent('click', this, e) === false){
30022             return;
30023         };
30024         
30025         this.parent().setActiveItem(this);
30026     },
30027     
30028     isActive: function () 
30029     {
30030         return this.active;
30031     },
30032     
30033     setActive : function(state)
30034     {
30035         if(this.active == state){
30036             return;
30037         }
30038         
30039         this.active = state;
30040         
30041         if (state) {
30042             this.el.addClass('active');
30043             return;
30044         }
30045         
30046         this.el.removeClass('active');
30047         
30048         return;
30049     },
30050     
30051     setDisabled : function(state)
30052     {
30053         if(this.disabled == state){
30054             return;
30055         }
30056         
30057         this.disabled = state;
30058         
30059         if (state) {
30060             this.el.addClass('disabled');
30061             return;
30062         }
30063         
30064         this.el.removeClass('disabled');
30065     },
30066     
30067     tooltipEl : function()
30068     {
30069         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30070     }
30071 });
30072  
30073
30074  /*
30075  * - LGPL
30076  *
30077  * FieldLabel
30078  * 
30079  */
30080
30081 /**
30082  * @class Roo.bootstrap.FieldLabel
30083  * @extends Roo.bootstrap.Component
30084  * Bootstrap FieldLabel class
30085  * @cfg {String} html contents of the element
30086  * @cfg {String} tag tag of the element default label
30087  * @cfg {String} cls class of the element
30088  * @cfg {String} target label target 
30089  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30090  * @cfg {String} invalidClass default "text-warning"
30091  * @cfg {String} validClass default "text-success"
30092  * @cfg {String} iconTooltip default "This field is required"
30093  * @cfg {String} indicatorpos (left|right) default left
30094  * 
30095  * @constructor
30096  * Create a new FieldLabel
30097  * @param {Object} config The config object
30098  */
30099
30100 Roo.bootstrap.FieldLabel = function(config){
30101     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30102     
30103     this.addEvents({
30104             /**
30105              * @event invalid
30106              * Fires after the field has been marked as invalid.
30107              * @param {Roo.form.FieldLabel} this
30108              * @param {String} msg The validation message
30109              */
30110             invalid : true,
30111             /**
30112              * @event valid
30113              * Fires after the field has been validated with no errors.
30114              * @param {Roo.form.FieldLabel} this
30115              */
30116             valid : true
30117         });
30118 };
30119
30120 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30121     
30122     tag: 'label',
30123     cls: '',
30124     html: '',
30125     target: '',
30126     allowBlank : true,
30127     invalidClass : 'has-warning',
30128     validClass : 'has-success',
30129     iconTooltip : 'This field is required',
30130     indicatorpos : 'left',
30131     
30132     getAutoCreate : function(){
30133         
30134         var cls = "";
30135         if (!this.allowBlank) {
30136             cls  = "visible";
30137         }
30138         
30139         var cfg = {
30140             tag : this.tag,
30141             cls : 'roo-bootstrap-field-label ' + this.cls,
30142             for : this.target,
30143             cn : [
30144                 {
30145                     tag : 'i',
30146                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30147                     tooltip : this.iconTooltip
30148                 },
30149                 {
30150                     tag : 'span',
30151                     html : this.html
30152                 }
30153             ] 
30154         };
30155         
30156         if(this.indicatorpos == 'right'){
30157             var cfg = {
30158                 tag : this.tag,
30159                 cls : 'roo-bootstrap-field-label ' + this.cls,
30160                 for : this.target,
30161                 cn : [
30162                     {
30163                         tag : 'span',
30164                         html : this.html
30165                     },
30166                     {
30167                         tag : 'i',
30168                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30169                         tooltip : this.iconTooltip
30170                     }
30171                 ] 
30172             };
30173         }
30174         
30175         return cfg;
30176     },
30177     
30178     initEvents: function() 
30179     {
30180         Roo.bootstrap.Element.superclass.initEvents.call(this);
30181         
30182         this.indicator = this.indicatorEl();
30183         
30184         if(this.indicator){
30185             this.indicator.removeClass('visible');
30186             this.indicator.addClass('invisible');
30187         }
30188         
30189         Roo.bootstrap.FieldLabel.register(this);
30190     },
30191     
30192     indicatorEl : function()
30193     {
30194         var indicator = this.el.select('i.roo-required-indicator',true).first();
30195         
30196         if(!indicator){
30197             return false;
30198         }
30199         
30200         return indicator;
30201         
30202     },
30203     
30204     /**
30205      * Mark this field as valid
30206      */
30207     markValid : function()
30208     {
30209         if(this.indicator){
30210             this.indicator.removeClass('visible');
30211             this.indicator.addClass('invisible');
30212         }
30213         
30214         this.el.removeClass(this.invalidClass);
30215         
30216         this.el.addClass(this.validClass);
30217         
30218         this.fireEvent('valid', this);
30219     },
30220     
30221     /**
30222      * Mark this field as invalid
30223      * @param {String} msg The validation message
30224      */
30225     markInvalid : function(msg)
30226     {
30227         if(this.indicator){
30228             this.indicator.removeClass('invisible');
30229             this.indicator.addClass('visible');
30230         }
30231         
30232         this.el.removeClass(this.validClass);
30233         
30234         this.el.addClass(this.invalidClass);
30235         
30236         this.fireEvent('invalid', this, msg);
30237     }
30238     
30239    
30240 });
30241
30242 Roo.apply(Roo.bootstrap.FieldLabel, {
30243     
30244     groups: {},
30245     
30246      /**
30247     * register a FieldLabel Group
30248     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30249     */
30250     register : function(label)
30251     {
30252         if(this.groups.hasOwnProperty(label.target)){
30253             return;
30254         }
30255      
30256         this.groups[label.target] = label;
30257         
30258     },
30259     /**
30260     * fetch a FieldLabel Group based on the target
30261     * @param {string} target
30262     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30263     */
30264     get: function(target) {
30265         if (typeof(this.groups[target]) == 'undefined') {
30266             return false;
30267         }
30268         
30269         return this.groups[target] ;
30270     }
30271 });
30272
30273  
30274
30275  /*
30276  * - LGPL
30277  *
30278  * page DateSplitField.
30279  * 
30280  */
30281
30282
30283 /**
30284  * @class Roo.bootstrap.DateSplitField
30285  * @extends Roo.bootstrap.Component
30286  * Bootstrap DateSplitField class
30287  * @cfg {string} fieldLabel - the label associated
30288  * @cfg {Number} labelWidth set the width of label (0-12)
30289  * @cfg {String} labelAlign (top|left)
30290  * @cfg {Boolean} dayAllowBlank (true|false) default false
30291  * @cfg {Boolean} monthAllowBlank (true|false) default false
30292  * @cfg {Boolean} yearAllowBlank (true|false) default false
30293  * @cfg {string} dayPlaceholder 
30294  * @cfg {string} monthPlaceholder
30295  * @cfg {string} yearPlaceholder
30296  * @cfg {string} dayFormat default 'd'
30297  * @cfg {string} monthFormat default 'm'
30298  * @cfg {string} yearFormat default 'Y'
30299  * @cfg {Number} labellg set the width of label (1-12)
30300  * @cfg {Number} labelmd set the width of label (1-12)
30301  * @cfg {Number} labelsm set the width of label (1-12)
30302  * @cfg {Number} labelxs set the width of label (1-12)
30303
30304  *     
30305  * @constructor
30306  * Create a new DateSplitField
30307  * @param {Object} config The config object
30308  */
30309
30310 Roo.bootstrap.DateSplitField = function(config){
30311     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30312     
30313     this.addEvents({
30314         // raw events
30315          /**
30316          * @event years
30317          * getting the data of years
30318          * @param {Roo.bootstrap.DateSplitField} this
30319          * @param {Object} years
30320          */
30321         "years" : true,
30322         /**
30323          * @event days
30324          * getting the data of days
30325          * @param {Roo.bootstrap.DateSplitField} this
30326          * @param {Object} days
30327          */
30328         "days" : true,
30329         /**
30330          * @event invalid
30331          * Fires after the field has been marked as invalid.
30332          * @param {Roo.form.Field} this
30333          * @param {String} msg The validation message
30334          */
30335         invalid : true,
30336        /**
30337          * @event valid
30338          * Fires after the field has been validated with no errors.
30339          * @param {Roo.form.Field} this
30340          */
30341         valid : true
30342     });
30343 };
30344
30345 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30346     
30347     fieldLabel : '',
30348     labelAlign : 'top',
30349     labelWidth : 3,
30350     dayAllowBlank : false,
30351     monthAllowBlank : false,
30352     yearAllowBlank : false,
30353     dayPlaceholder : '',
30354     monthPlaceholder : '',
30355     yearPlaceholder : '',
30356     dayFormat : 'd',
30357     monthFormat : 'm',
30358     yearFormat : 'Y',
30359     isFormField : true,
30360     labellg : 0,
30361     labelmd : 0,
30362     labelsm : 0,
30363     labelxs : 0,
30364     
30365     getAutoCreate : function()
30366     {
30367         var cfg = {
30368             tag : 'div',
30369             cls : 'row roo-date-split-field-group',
30370             cn : [
30371                 {
30372                     tag : 'input',
30373                     type : 'hidden',
30374                     cls : 'form-hidden-field roo-date-split-field-group-value',
30375                     name : this.name
30376                 }
30377             ]
30378         };
30379         
30380         var labelCls = 'col-md-12';
30381         var contentCls = 'col-md-4';
30382         
30383         if(this.fieldLabel){
30384             
30385             var label = {
30386                 tag : 'div',
30387                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30388                 cn : [
30389                     {
30390                         tag : 'label',
30391                         html : this.fieldLabel
30392                     }
30393                 ]
30394             };
30395             
30396             if(this.labelAlign == 'left'){
30397             
30398                 if(this.labelWidth > 12){
30399                     label.style = "width: " + this.labelWidth + 'px';
30400                 }
30401
30402                 if(this.labelWidth < 13 && this.labelmd == 0){
30403                     this.labelmd = this.labelWidth;
30404                 }
30405
30406                 if(this.labellg > 0){
30407                     labelCls = ' col-lg-' + this.labellg;
30408                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30409                 }
30410
30411                 if(this.labelmd > 0){
30412                     labelCls = ' col-md-' + this.labelmd;
30413                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30414                 }
30415
30416                 if(this.labelsm > 0){
30417                     labelCls = ' col-sm-' + this.labelsm;
30418                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30419                 }
30420
30421                 if(this.labelxs > 0){
30422                     labelCls = ' col-xs-' + this.labelxs;
30423                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30424                 }
30425             }
30426             
30427             label.cls += ' ' + labelCls;
30428             
30429             cfg.cn.push(label);
30430         }
30431         
30432         Roo.each(['day', 'month', 'year'], function(t){
30433             cfg.cn.push({
30434                 tag : 'div',
30435                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30436             });
30437         }, this);
30438         
30439         return cfg;
30440     },
30441     
30442     inputEl: function ()
30443     {
30444         return this.el.select('.roo-date-split-field-group-value', true).first();
30445     },
30446     
30447     onRender : function(ct, position) 
30448     {
30449         var _this = this;
30450         
30451         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30452         
30453         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30454         
30455         this.dayField = new Roo.bootstrap.ComboBox({
30456             allowBlank : this.dayAllowBlank,
30457             alwaysQuery : true,
30458             displayField : 'value',
30459             editable : false,
30460             fieldLabel : '',
30461             forceSelection : true,
30462             mode : 'local',
30463             placeholder : this.dayPlaceholder,
30464             selectOnFocus : true,
30465             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30466             triggerAction : 'all',
30467             typeAhead : true,
30468             valueField : 'value',
30469             store : new Roo.data.SimpleStore({
30470                 data : (function() {    
30471                     var days = [];
30472                     _this.fireEvent('days', _this, days);
30473                     return days;
30474                 })(),
30475                 fields : [ 'value' ]
30476             }),
30477             listeners : {
30478                 select : function (_self, record, index)
30479                 {
30480                     _this.setValue(_this.getValue());
30481                 }
30482             }
30483         });
30484
30485         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30486         
30487         this.monthField = new Roo.bootstrap.MonthField({
30488             after : '<i class=\"fa fa-calendar\"></i>',
30489             allowBlank : this.monthAllowBlank,
30490             placeholder : this.monthPlaceholder,
30491             readOnly : true,
30492             listeners : {
30493                 render : function (_self)
30494                 {
30495                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30496                         e.preventDefault();
30497                         _self.focus();
30498                     });
30499                 },
30500                 select : function (_self, oldvalue, newvalue)
30501                 {
30502                     _this.setValue(_this.getValue());
30503                 }
30504             }
30505         });
30506         
30507         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30508         
30509         this.yearField = new Roo.bootstrap.ComboBox({
30510             allowBlank : this.yearAllowBlank,
30511             alwaysQuery : true,
30512             displayField : 'value',
30513             editable : false,
30514             fieldLabel : '',
30515             forceSelection : true,
30516             mode : 'local',
30517             placeholder : this.yearPlaceholder,
30518             selectOnFocus : true,
30519             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30520             triggerAction : 'all',
30521             typeAhead : true,
30522             valueField : 'value',
30523             store : new Roo.data.SimpleStore({
30524                 data : (function() {
30525                     var years = [];
30526                     _this.fireEvent('years', _this, years);
30527                     return years;
30528                 })(),
30529                 fields : [ 'value' ]
30530             }),
30531             listeners : {
30532                 select : function (_self, record, index)
30533                 {
30534                     _this.setValue(_this.getValue());
30535                 }
30536             }
30537         });
30538
30539         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30540     },
30541     
30542     setValue : function(v, format)
30543     {
30544         this.inputEl.dom.value = v;
30545         
30546         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30547         
30548         var d = Date.parseDate(v, f);
30549         
30550         if(!d){
30551             this.validate();
30552             return;
30553         }
30554         
30555         this.setDay(d.format(this.dayFormat));
30556         this.setMonth(d.format(this.monthFormat));
30557         this.setYear(d.format(this.yearFormat));
30558         
30559         this.validate();
30560         
30561         return;
30562     },
30563     
30564     setDay : function(v)
30565     {
30566         this.dayField.setValue(v);
30567         this.inputEl.dom.value = this.getValue();
30568         this.validate();
30569         return;
30570     },
30571     
30572     setMonth : function(v)
30573     {
30574         this.monthField.setValue(v, true);
30575         this.inputEl.dom.value = this.getValue();
30576         this.validate();
30577         return;
30578     },
30579     
30580     setYear : function(v)
30581     {
30582         this.yearField.setValue(v);
30583         this.inputEl.dom.value = this.getValue();
30584         this.validate();
30585         return;
30586     },
30587     
30588     getDay : function()
30589     {
30590         return this.dayField.getValue();
30591     },
30592     
30593     getMonth : function()
30594     {
30595         return this.monthField.getValue();
30596     },
30597     
30598     getYear : function()
30599     {
30600         return this.yearField.getValue();
30601     },
30602     
30603     getValue : function()
30604     {
30605         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30606         
30607         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30608         
30609         return date;
30610     },
30611     
30612     reset : function()
30613     {
30614         this.setDay('');
30615         this.setMonth('');
30616         this.setYear('');
30617         this.inputEl.dom.value = '';
30618         this.validate();
30619         return;
30620     },
30621     
30622     validate : function()
30623     {
30624         var d = this.dayField.validate();
30625         var m = this.monthField.validate();
30626         var y = this.yearField.validate();
30627         
30628         var valid = true;
30629         
30630         if(
30631                 (!this.dayAllowBlank && !d) ||
30632                 (!this.monthAllowBlank && !m) ||
30633                 (!this.yearAllowBlank && !y)
30634         ){
30635             valid = false;
30636         }
30637         
30638         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30639             return valid;
30640         }
30641         
30642         if(valid){
30643             this.markValid();
30644             return valid;
30645         }
30646         
30647         this.markInvalid();
30648         
30649         return valid;
30650     },
30651     
30652     markValid : function()
30653     {
30654         
30655         var label = this.el.select('label', true).first();
30656         var icon = this.el.select('i.fa-star', true).first();
30657
30658         if(label && icon){
30659             icon.remove();
30660         }
30661         
30662         this.fireEvent('valid', this);
30663     },
30664     
30665      /**
30666      * Mark this field as invalid
30667      * @param {String} msg The validation message
30668      */
30669     markInvalid : function(msg)
30670     {
30671         
30672         var label = this.el.select('label', true).first();
30673         var icon = this.el.select('i.fa-star', true).first();
30674
30675         if(label && !icon){
30676             this.el.select('.roo-date-split-field-label', true).createChild({
30677                 tag : 'i',
30678                 cls : 'text-danger fa fa-lg fa-star',
30679                 tooltip : 'This field is required',
30680                 style : 'margin-right:5px;'
30681             }, label, true);
30682         }
30683         
30684         this.fireEvent('invalid', this, msg);
30685     },
30686     
30687     clearInvalid : function()
30688     {
30689         var label = this.el.select('label', true).first();
30690         var icon = this.el.select('i.fa-star', true).first();
30691
30692         if(label && icon){
30693             icon.remove();
30694         }
30695         
30696         this.fireEvent('valid', this);
30697     },
30698     
30699     getName: function()
30700     {
30701         return this.name;
30702     }
30703     
30704 });
30705
30706  /**
30707  *
30708  * This is based on 
30709  * http://masonry.desandro.com
30710  *
30711  * The idea is to render all the bricks based on vertical width...
30712  *
30713  * The original code extends 'outlayer' - we might need to use that....
30714  * 
30715  */
30716
30717
30718 /**
30719  * @class Roo.bootstrap.LayoutMasonry
30720  * @extends Roo.bootstrap.Component
30721  * Bootstrap Layout Masonry class
30722  * 
30723  * @constructor
30724  * Create a new Element
30725  * @param {Object} config The config object
30726  */
30727
30728 Roo.bootstrap.LayoutMasonry = function(config){
30729     
30730     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30731     
30732     this.bricks = [];
30733     
30734     Roo.bootstrap.LayoutMasonry.register(this);
30735     
30736     this.addEvents({
30737         // raw events
30738         /**
30739          * @event layout
30740          * Fire after layout the items
30741          * @param {Roo.bootstrap.LayoutMasonry} this
30742          * @param {Roo.EventObject} e
30743          */
30744         "layout" : true
30745     });
30746     
30747 };
30748
30749 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30750     
30751     /**
30752      * @cfg {Boolean} isLayoutInstant = no animation?
30753      */   
30754     isLayoutInstant : false, // needed?
30755    
30756     /**
30757      * @cfg {Number} boxWidth  width of the columns
30758      */   
30759     boxWidth : 450,
30760     
30761       /**
30762      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30763      */   
30764     boxHeight : 0,
30765     
30766     /**
30767      * @cfg {Number} padWidth padding below box..
30768      */   
30769     padWidth : 10, 
30770     
30771     /**
30772      * @cfg {Number} gutter gutter width..
30773      */   
30774     gutter : 10,
30775     
30776      /**
30777      * @cfg {Number} maxCols maximum number of columns
30778      */   
30779     
30780     maxCols: 0,
30781     
30782     /**
30783      * @cfg {Boolean} isAutoInitial defalut true
30784      */   
30785     isAutoInitial : true, 
30786     
30787     containerWidth: 0,
30788     
30789     /**
30790      * @cfg {Boolean} isHorizontal defalut false
30791      */   
30792     isHorizontal : false, 
30793
30794     currentSize : null,
30795     
30796     tag: 'div',
30797     
30798     cls: '',
30799     
30800     bricks: null, //CompositeElement
30801     
30802     cols : 1,
30803     
30804     _isLayoutInited : false,
30805     
30806 //    isAlternative : false, // only use for vertical layout...
30807     
30808     /**
30809      * @cfg {Number} alternativePadWidth padding below box..
30810      */   
30811     alternativePadWidth : 50,
30812     
30813     selectedBrick : [],
30814     
30815     getAutoCreate : function(){
30816         
30817         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30818         
30819         var cfg = {
30820             tag: this.tag,
30821             cls: 'blog-masonary-wrapper ' + this.cls,
30822             cn : {
30823                 cls : 'mas-boxes masonary'
30824             }
30825         };
30826         
30827         return cfg;
30828     },
30829     
30830     getChildContainer: function( )
30831     {
30832         if (this.boxesEl) {
30833             return this.boxesEl;
30834         }
30835         
30836         this.boxesEl = this.el.select('.mas-boxes').first();
30837         
30838         return this.boxesEl;
30839     },
30840     
30841     
30842     initEvents : function()
30843     {
30844         var _this = this;
30845         
30846         if(this.isAutoInitial){
30847             Roo.log('hook children rendered');
30848             this.on('childrenrendered', function() {
30849                 Roo.log('children rendered');
30850                 _this.initial();
30851             } ,this);
30852         }
30853     },
30854     
30855     initial : function()
30856     {
30857         this.selectedBrick = [];
30858         
30859         this.currentSize = this.el.getBox(true);
30860         
30861         Roo.EventManager.onWindowResize(this.resize, this); 
30862
30863         if(!this.isAutoInitial){
30864             this.layout();
30865             return;
30866         }
30867         
30868         this.layout();
30869         
30870         return;
30871         //this.layout.defer(500,this);
30872         
30873     },
30874     
30875     resize : function()
30876     {
30877         var cs = this.el.getBox(true);
30878         
30879         if (
30880                 this.currentSize.width == cs.width && 
30881                 this.currentSize.x == cs.x && 
30882                 this.currentSize.height == cs.height && 
30883                 this.currentSize.y == cs.y 
30884         ) {
30885             Roo.log("no change in with or X or Y");
30886             return;
30887         }
30888         
30889         this.currentSize = cs;
30890         
30891         this.layout();
30892         
30893     },
30894     
30895     layout : function()
30896     {   
30897         this._resetLayout();
30898         
30899         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30900         
30901         this.layoutItems( isInstant );
30902       
30903         this._isLayoutInited = true;
30904         
30905         this.fireEvent('layout', this);
30906         
30907     },
30908     
30909     _resetLayout : function()
30910     {
30911         if(this.isHorizontal){
30912             this.horizontalMeasureColumns();
30913             return;
30914         }
30915         
30916         this.verticalMeasureColumns();
30917         
30918     },
30919     
30920     verticalMeasureColumns : function()
30921     {
30922         this.getContainerWidth();
30923         
30924 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30925 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30926 //            return;
30927 //        }
30928         
30929         var boxWidth = this.boxWidth + this.padWidth;
30930         
30931         if(this.containerWidth < this.boxWidth){
30932             boxWidth = this.containerWidth
30933         }
30934         
30935         var containerWidth = this.containerWidth;
30936         
30937         var cols = Math.floor(containerWidth / boxWidth);
30938         
30939         this.cols = Math.max( cols, 1 );
30940         
30941         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30942         
30943         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30944         
30945         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30946         
30947         this.colWidth = boxWidth + avail - this.padWidth;
30948         
30949         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30950         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30951     },
30952     
30953     horizontalMeasureColumns : function()
30954     {
30955         this.getContainerWidth();
30956         
30957         var boxWidth = this.boxWidth;
30958         
30959         if(this.containerWidth < boxWidth){
30960             boxWidth = this.containerWidth;
30961         }
30962         
30963         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30964         
30965         this.el.setHeight(boxWidth);
30966         
30967     },
30968     
30969     getContainerWidth : function()
30970     {
30971         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30972     },
30973     
30974     layoutItems : function( isInstant )
30975     {
30976         Roo.log(this.bricks);
30977         
30978         var items = Roo.apply([], this.bricks);
30979         
30980         if(this.isHorizontal){
30981             this._horizontalLayoutItems( items , isInstant );
30982             return;
30983         }
30984         
30985 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30986 //            this._verticalAlternativeLayoutItems( items , isInstant );
30987 //            return;
30988 //        }
30989         
30990         this._verticalLayoutItems( items , isInstant );
30991         
30992     },
30993     
30994     _verticalLayoutItems : function ( items , isInstant)
30995     {
30996         if ( !items || !items.length ) {
30997             return;
30998         }
30999         
31000         var standard = [
31001             ['xs', 'xs', 'xs', 'tall'],
31002             ['xs', 'xs', 'tall'],
31003             ['xs', 'xs', 'sm'],
31004             ['xs', 'xs', 'xs'],
31005             ['xs', 'tall'],
31006             ['xs', 'sm'],
31007             ['xs', 'xs'],
31008             ['xs'],
31009             
31010             ['sm', 'xs', 'xs'],
31011             ['sm', 'xs'],
31012             ['sm'],
31013             
31014             ['tall', 'xs', 'xs', 'xs'],
31015             ['tall', 'xs', 'xs'],
31016             ['tall', 'xs'],
31017             ['tall']
31018             
31019         ];
31020         
31021         var queue = [];
31022         
31023         var boxes = [];
31024         
31025         var box = [];
31026         
31027         Roo.each(items, function(item, k){
31028             
31029             switch (item.size) {
31030                 // these layouts take up a full box,
31031                 case 'md' :
31032                 case 'md-left' :
31033                 case 'md-right' :
31034                 case 'wide' :
31035                     
31036                     if(box.length){
31037                         boxes.push(box);
31038                         box = [];
31039                     }
31040                     
31041                     boxes.push([item]);
31042                     
31043                     break;
31044                     
31045                 case 'xs' :
31046                 case 'sm' :
31047                 case 'tall' :
31048                     
31049                     box.push(item);
31050                     
31051                     break;
31052                 default :
31053                     break;
31054                     
31055             }
31056             
31057         }, this);
31058         
31059         if(box.length){
31060             boxes.push(box);
31061             box = [];
31062         }
31063         
31064         var filterPattern = function(box, length)
31065         {
31066             if(!box.length){
31067                 return;
31068             }
31069             
31070             var match = false;
31071             
31072             var pattern = box.slice(0, length);
31073             
31074             var format = [];
31075             
31076             Roo.each(pattern, function(i){
31077                 format.push(i.size);
31078             }, this);
31079             
31080             Roo.each(standard, function(s){
31081                 
31082                 if(String(s) != String(format)){
31083                     return;
31084                 }
31085                 
31086                 match = true;
31087                 return false;
31088                 
31089             }, this);
31090             
31091             if(!match && length == 1){
31092                 return;
31093             }
31094             
31095             if(!match){
31096                 filterPattern(box, length - 1);
31097                 return;
31098             }
31099                 
31100             queue.push(pattern);
31101
31102             box = box.slice(length, box.length);
31103
31104             filterPattern(box, 4);
31105
31106             return;
31107             
31108         }
31109         
31110         Roo.each(boxes, function(box, k){
31111             
31112             if(!box.length){
31113                 return;
31114             }
31115             
31116             if(box.length == 1){
31117                 queue.push(box);
31118                 return;
31119             }
31120             
31121             filterPattern(box, 4);
31122             
31123         }, this);
31124         
31125         this._processVerticalLayoutQueue( queue, isInstant );
31126         
31127     },
31128     
31129 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31130 //    {
31131 //        if ( !items || !items.length ) {
31132 //            return;
31133 //        }
31134 //
31135 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31136 //        
31137 //    },
31138     
31139     _horizontalLayoutItems : function ( items , isInstant)
31140     {
31141         if ( !items || !items.length || items.length < 3) {
31142             return;
31143         }
31144         
31145         items.reverse();
31146         
31147         var eItems = items.slice(0, 3);
31148         
31149         items = items.slice(3, items.length);
31150         
31151         var standard = [
31152             ['xs', 'xs', 'xs', 'wide'],
31153             ['xs', 'xs', 'wide'],
31154             ['xs', 'xs', 'sm'],
31155             ['xs', 'xs', 'xs'],
31156             ['xs', 'wide'],
31157             ['xs', 'sm'],
31158             ['xs', 'xs'],
31159             ['xs'],
31160             
31161             ['sm', 'xs', 'xs'],
31162             ['sm', 'xs'],
31163             ['sm'],
31164             
31165             ['wide', 'xs', 'xs', 'xs'],
31166             ['wide', 'xs', 'xs'],
31167             ['wide', 'xs'],
31168             ['wide'],
31169             
31170             ['wide-thin']
31171         ];
31172         
31173         var queue = [];
31174         
31175         var boxes = [];
31176         
31177         var box = [];
31178         
31179         Roo.each(items, function(item, k){
31180             
31181             switch (item.size) {
31182                 case 'md' :
31183                 case 'md-left' :
31184                 case 'md-right' :
31185                 case 'tall' :
31186                     
31187                     if(box.length){
31188                         boxes.push(box);
31189                         box = [];
31190                     }
31191                     
31192                     boxes.push([item]);
31193                     
31194                     break;
31195                     
31196                 case 'xs' :
31197                 case 'sm' :
31198                 case 'wide' :
31199                 case 'wide-thin' :
31200                     
31201                     box.push(item);
31202                     
31203                     break;
31204                 default :
31205                     break;
31206                     
31207             }
31208             
31209         }, this);
31210         
31211         if(box.length){
31212             boxes.push(box);
31213             box = [];
31214         }
31215         
31216         var filterPattern = function(box, length)
31217         {
31218             if(!box.length){
31219                 return;
31220             }
31221             
31222             var match = false;
31223             
31224             var pattern = box.slice(0, length);
31225             
31226             var format = [];
31227             
31228             Roo.each(pattern, function(i){
31229                 format.push(i.size);
31230             }, this);
31231             
31232             Roo.each(standard, function(s){
31233                 
31234                 if(String(s) != String(format)){
31235                     return;
31236                 }
31237                 
31238                 match = true;
31239                 return false;
31240                 
31241             }, this);
31242             
31243             if(!match && length == 1){
31244                 return;
31245             }
31246             
31247             if(!match){
31248                 filterPattern(box, length - 1);
31249                 return;
31250             }
31251                 
31252             queue.push(pattern);
31253
31254             box = box.slice(length, box.length);
31255
31256             filterPattern(box, 4);
31257
31258             return;
31259             
31260         }
31261         
31262         Roo.each(boxes, function(box, k){
31263             
31264             if(!box.length){
31265                 return;
31266             }
31267             
31268             if(box.length == 1){
31269                 queue.push(box);
31270                 return;
31271             }
31272             
31273             filterPattern(box, 4);
31274             
31275         }, this);
31276         
31277         
31278         var prune = [];
31279         
31280         var pos = this.el.getBox(true);
31281         
31282         var minX = pos.x;
31283         
31284         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31285         
31286         var hit_end = false;
31287         
31288         Roo.each(queue, function(box){
31289             
31290             if(hit_end){
31291                 
31292                 Roo.each(box, function(b){
31293                 
31294                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31295                     b.el.hide();
31296
31297                 }, this);
31298
31299                 return;
31300             }
31301             
31302             var mx = 0;
31303             
31304             Roo.each(box, function(b){
31305                 
31306                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31307                 b.el.show();
31308
31309                 mx = Math.max(mx, b.x);
31310                 
31311             }, this);
31312             
31313             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31314             
31315             if(maxX < minX){
31316                 
31317                 Roo.each(box, function(b){
31318                 
31319                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31320                     b.el.hide();
31321                     
31322                 }, this);
31323                 
31324                 hit_end = true;
31325                 
31326                 return;
31327             }
31328             
31329             prune.push(box);
31330             
31331         }, this);
31332         
31333         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31334     },
31335     
31336     /** Sets position of item in DOM
31337     * @param {Element} item
31338     * @param {Number} x - horizontal position
31339     * @param {Number} y - vertical position
31340     * @param {Boolean} isInstant - disables transitions
31341     */
31342     _processVerticalLayoutQueue : function( queue, isInstant )
31343     {
31344         var pos = this.el.getBox(true);
31345         var x = pos.x;
31346         var y = pos.y;
31347         var maxY = [];
31348         
31349         for (var i = 0; i < this.cols; i++){
31350             maxY[i] = pos.y;
31351         }
31352         
31353         Roo.each(queue, function(box, k){
31354             
31355             var col = k % this.cols;
31356             
31357             Roo.each(box, function(b,kk){
31358                 
31359                 b.el.position('absolute');
31360                 
31361                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31362                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31363                 
31364                 if(b.size == 'md-left' || b.size == 'md-right'){
31365                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31366                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31367                 }
31368                 
31369                 b.el.setWidth(width);
31370                 b.el.setHeight(height);
31371                 // iframe?
31372                 b.el.select('iframe',true).setSize(width,height);
31373                 
31374             }, this);
31375             
31376             for (var i = 0; i < this.cols; i++){
31377                 
31378                 if(maxY[i] < maxY[col]){
31379                     col = i;
31380                     continue;
31381                 }
31382                 
31383                 col = Math.min(col, i);
31384                 
31385             }
31386             
31387             x = pos.x + col * (this.colWidth + this.padWidth);
31388             
31389             y = maxY[col];
31390             
31391             var positions = [];
31392             
31393             switch (box.length){
31394                 case 1 :
31395                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31396                     break;
31397                 case 2 :
31398                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31399                     break;
31400                 case 3 :
31401                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31402                     break;
31403                 case 4 :
31404                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31405                     break;
31406                 default :
31407                     break;
31408             }
31409             
31410             Roo.each(box, function(b,kk){
31411                 
31412                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31413                 
31414                 var sz = b.el.getSize();
31415                 
31416                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31417                 
31418             }, this);
31419             
31420         }, this);
31421         
31422         var mY = 0;
31423         
31424         for (var i = 0; i < this.cols; i++){
31425             mY = Math.max(mY, maxY[i]);
31426         }
31427         
31428         this.el.setHeight(mY - pos.y);
31429         
31430     },
31431     
31432 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31433 //    {
31434 //        var pos = this.el.getBox(true);
31435 //        var x = pos.x;
31436 //        var y = pos.y;
31437 //        var maxX = pos.right;
31438 //        
31439 //        var maxHeight = 0;
31440 //        
31441 //        Roo.each(items, function(item, k){
31442 //            
31443 //            var c = k % 2;
31444 //            
31445 //            item.el.position('absolute');
31446 //                
31447 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31448 //
31449 //            item.el.setWidth(width);
31450 //
31451 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31452 //
31453 //            item.el.setHeight(height);
31454 //            
31455 //            if(c == 0){
31456 //                item.el.setXY([x, y], isInstant ? false : true);
31457 //            } else {
31458 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31459 //            }
31460 //            
31461 //            y = y + height + this.alternativePadWidth;
31462 //            
31463 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31464 //            
31465 //        }, this);
31466 //        
31467 //        this.el.setHeight(maxHeight);
31468 //        
31469 //    },
31470     
31471     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31472     {
31473         var pos = this.el.getBox(true);
31474         
31475         var minX = pos.x;
31476         var minY = pos.y;
31477         
31478         var maxX = pos.right;
31479         
31480         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31481         
31482         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31483         
31484         Roo.each(queue, function(box, k){
31485             
31486             Roo.each(box, function(b, kk){
31487                 
31488                 b.el.position('absolute');
31489                 
31490                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31491                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31492                 
31493                 if(b.size == 'md-left' || b.size == 'md-right'){
31494                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31495                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31496                 }
31497                 
31498                 b.el.setWidth(width);
31499                 b.el.setHeight(height);
31500                 
31501             }, this);
31502             
31503             if(!box.length){
31504                 return;
31505             }
31506             
31507             var positions = [];
31508             
31509             switch (box.length){
31510                 case 1 :
31511                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31512                     break;
31513                 case 2 :
31514                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31515                     break;
31516                 case 3 :
31517                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31518                     break;
31519                 case 4 :
31520                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31521                     break;
31522                 default :
31523                     break;
31524             }
31525             
31526             Roo.each(box, function(b,kk){
31527                 
31528                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31529                 
31530                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31531                 
31532             }, this);
31533             
31534         }, this);
31535         
31536     },
31537     
31538     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31539     {
31540         Roo.each(eItems, function(b,k){
31541             
31542             b.size = (k == 0) ? 'sm' : 'xs';
31543             b.x = (k == 0) ? 2 : 1;
31544             b.y = (k == 0) ? 2 : 1;
31545             
31546             b.el.position('absolute');
31547             
31548             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31549                 
31550             b.el.setWidth(width);
31551             
31552             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31553             
31554             b.el.setHeight(height);
31555             
31556         }, this);
31557
31558         var positions = [];
31559         
31560         positions.push({
31561             x : maxX - this.unitWidth * 2 - this.gutter,
31562             y : minY
31563         });
31564         
31565         positions.push({
31566             x : maxX - this.unitWidth,
31567             y : minY + (this.unitWidth + this.gutter) * 2
31568         });
31569         
31570         positions.push({
31571             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31572             y : minY
31573         });
31574         
31575         Roo.each(eItems, function(b,k){
31576             
31577             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31578
31579         }, this);
31580         
31581     },
31582     
31583     getVerticalOneBoxColPositions : function(x, y, box)
31584     {
31585         var pos = [];
31586         
31587         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31588         
31589         if(box[0].size == 'md-left'){
31590             rand = 0;
31591         }
31592         
31593         if(box[0].size == 'md-right'){
31594             rand = 1;
31595         }
31596         
31597         pos.push({
31598             x : x + (this.unitWidth + this.gutter) * rand,
31599             y : y
31600         });
31601         
31602         return pos;
31603     },
31604     
31605     getVerticalTwoBoxColPositions : function(x, y, box)
31606     {
31607         var pos = [];
31608         
31609         if(box[0].size == 'xs'){
31610             
31611             pos.push({
31612                 x : x,
31613                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31614             });
31615
31616             pos.push({
31617                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31618                 y : y
31619             });
31620             
31621             return pos;
31622             
31623         }
31624         
31625         pos.push({
31626             x : x,
31627             y : y
31628         });
31629
31630         pos.push({
31631             x : x + (this.unitWidth + this.gutter) * 2,
31632             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31633         });
31634         
31635         return pos;
31636         
31637     },
31638     
31639     getVerticalThreeBoxColPositions : function(x, y, box)
31640     {
31641         var pos = [];
31642         
31643         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31644             
31645             pos.push({
31646                 x : x,
31647                 y : y
31648             });
31649
31650             pos.push({
31651                 x : x + (this.unitWidth + this.gutter) * 1,
31652                 y : y
31653             });
31654             
31655             pos.push({
31656                 x : x + (this.unitWidth + this.gutter) * 2,
31657                 y : y
31658             });
31659             
31660             return pos;
31661             
31662         }
31663         
31664         if(box[0].size == 'xs' && box[1].size == 'xs'){
31665             
31666             pos.push({
31667                 x : x,
31668                 y : y
31669             });
31670
31671             pos.push({
31672                 x : x,
31673                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31674             });
31675             
31676             pos.push({
31677                 x : x + (this.unitWidth + this.gutter) * 1,
31678                 y : y
31679             });
31680             
31681             return pos;
31682             
31683         }
31684         
31685         pos.push({
31686             x : x,
31687             y : y
31688         });
31689
31690         pos.push({
31691             x : x + (this.unitWidth + this.gutter) * 2,
31692             y : y
31693         });
31694
31695         pos.push({
31696             x : x + (this.unitWidth + this.gutter) * 2,
31697             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31698         });
31699             
31700         return pos;
31701         
31702     },
31703     
31704     getVerticalFourBoxColPositions : function(x, y, box)
31705     {
31706         var pos = [];
31707         
31708         if(box[0].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) * 1
31718             });
31719             
31720             pos.push({
31721                 x : x,
31722                 y : y + (this.unitHeight + this.gutter) * 2
31723             });
31724             
31725             pos.push({
31726                 x : x + (this.unitWidth + this.gutter) * 1,
31727                 y : y
31728             });
31729             
31730             return pos;
31731             
31732         }
31733         
31734         pos.push({
31735             x : x,
31736             y : y
31737         });
31738
31739         pos.push({
31740             x : x + (this.unitWidth + this.gutter) * 2,
31741             y : y
31742         });
31743
31744         pos.push({
31745             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31746             y : y + (this.unitHeight + this.gutter) * 1
31747         });
31748
31749         pos.push({
31750             x : x + (this.unitWidth + this.gutter) * 2,
31751             y : y + (this.unitWidth + this.gutter) * 2
31752         });
31753
31754         return pos;
31755         
31756     },
31757     
31758     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31759     {
31760         var pos = [];
31761         
31762         if(box[0].size == 'md-left'){
31763             pos.push({
31764                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31765                 y : minY
31766             });
31767             
31768             return pos;
31769         }
31770         
31771         if(box[0].size == 'md-right'){
31772             pos.push({
31773                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31774                 y : minY + (this.unitWidth + this.gutter) * 1
31775             });
31776             
31777             return pos;
31778         }
31779         
31780         var rand = Math.floor(Math.random() * (4 - box[0].y));
31781         
31782         pos.push({
31783             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31784             y : minY + (this.unitWidth + this.gutter) * rand
31785         });
31786         
31787         return pos;
31788         
31789     },
31790     
31791     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31792     {
31793         var pos = [];
31794         
31795         if(box[0].size == 'xs'){
31796             
31797             pos.push({
31798                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31799                 y : minY
31800             });
31801
31802             pos.push({
31803                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31804                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31805             });
31806             
31807             return pos;
31808             
31809         }
31810         
31811         pos.push({
31812             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31813             y : minY
31814         });
31815
31816         pos.push({
31817             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31818             y : minY + (this.unitWidth + this.gutter) * 2
31819         });
31820         
31821         return pos;
31822         
31823     },
31824     
31825     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31826     {
31827         var pos = [];
31828         
31829         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31830             
31831             pos.push({
31832                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31833                 y : minY
31834             });
31835
31836             pos.push({
31837                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31838                 y : minY + (this.unitWidth + this.gutter) * 1
31839             });
31840             
31841             pos.push({
31842                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31843                 y : minY + (this.unitWidth + this.gutter) * 2
31844             });
31845             
31846             return pos;
31847             
31848         }
31849         
31850         if(box[0].size == 'xs' && box[1].size == 'xs'){
31851             
31852             pos.push({
31853                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31854                 y : minY
31855             });
31856
31857             pos.push({
31858                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31859                 y : minY
31860             });
31861             
31862             pos.push({
31863                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31864                 y : minY + (this.unitWidth + this.gutter) * 1
31865             });
31866             
31867             return pos;
31868             
31869         }
31870         
31871         pos.push({
31872             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31873             y : minY
31874         });
31875
31876         pos.push({
31877             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31878             y : minY + (this.unitWidth + this.gutter) * 2
31879         });
31880
31881         pos.push({
31882             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31883             y : minY + (this.unitWidth + this.gutter) * 2
31884         });
31885             
31886         return pos;
31887         
31888     },
31889     
31890     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31891     {
31892         var pos = [];
31893         
31894         if(box[0].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[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),
31908                 y : minY
31909             });
31910             
31911             pos.push({
31912                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31913                 y : minY + (this.unitWidth + this.gutter) * 1
31914             });
31915             
31916             return pos;
31917             
31918         }
31919         
31920         pos.push({
31921             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31922             y : minY
31923         });
31924         
31925         pos.push({
31926             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31927             y : minY + (this.unitWidth + this.gutter) * 2
31928         });
31929         
31930         pos.push({
31931             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31932             y : minY + (this.unitWidth + this.gutter) * 2
31933         });
31934         
31935         pos.push({
31936             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),
31937             y : minY + (this.unitWidth + this.gutter) * 2
31938         });
31939
31940         return pos;
31941         
31942     },
31943     
31944     /**
31945     * remove a Masonry Brick
31946     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31947     */
31948     removeBrick : function(brick_id)
31949     {
31950         if (!brick_id) {
31951             return;
31952         }
31953         
31954         for (var i = 0; i<this.bricks.length; i++) {
31955             if (this.bricks[i].id == brick_id) {
31956                 this.bricks.splice(i,1);
31957                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31958                 this.initial();
31959             }
31960         }
31961     },
31962     
31963     /**
31964     * adds a Masonry Brick
31965     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31966     */
31967     addBrick : function(cfg)
31968     {
31969         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31970         //this.register(cn);
31971         cn.parentId = this.id;
31972         cn.onRender(this.el, null);
31973         return cn;
31974     },
31975     
31976     /**
31977     * register a Masonry Brick
31978     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31979     */
31980     
31981     register : function(brick)
31982     {
31983         this.bricks.push(brick);
31984         brick.masonryId = this.id;
31985     },
31986     
31987     /**
31988     * clear all the Masonry Brick
31989     */
31990     clearAll : function()
31991     {
31992         this.bricks = [];
31993         //this.getChildContainer().dom.innerHTML = "";
31994         this.el.dom.innerHTML = '';
31995     },
31996     
31997     getSelected : function()
31998     {
31999         if (!this.selectedBrick) {
32000             return false;
32001         }
32002         
32003         return this.selectedBrick;
32004     }
32005 });
32006
32007 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32008     
32009     groups: {},
32010      /**
32011     * register a Masonry Layout
32012     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32013     */
32014     
32015     register : function(layout)
32016     {
32017         this.groups[layout.id] = layout;
32018     },
32019     /**
32020     * fetch a  Masonry Layout based on the masonry layout ID
32021     * @param {string} the masonry layout to add
32022     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32023     */
32024     
32025     get: function(layout_id) {
32026         if (typeof(this.groups[layout_id]) == 'undefined') {
32027             return false;
32028         }
32029         return this.groups[layout_id] ;
32030     }
32031     
32032     
32033     
32034 });
32035
32036  
32037
32038  /**
32039  *
32040  * This is based on 
32041  * http://masonry.desandro.com
32042  *
32043  * The idea is to render all the bricks based on vertical width...
32044  *
32045  * The original code extends 'outlayer' - we might need to use that....
32046  * 
32047  */
32048
32049
32050 /**
32051  * @class Roo.bootstrap.LayoutMasonryAuto
32052  * @extends Roo.bootstrap.Component
32053  * Bootstrap Layout Masonry class
32054  * 
32055  * @constructor
32056  * Create a new Element
32057  * @param {Object} config The config object
32058  */
32059
32060 Roo.bootstrap.LayoutMasonryAuto = function(config){
32061     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32062 };
32063
32064 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32065     
32066       /**
32067      * @cfg {Boolean} isFitWidth  - resize the width..
32068      */   
32069     isFitWidth : false,  // options..
32070     /**
32071      * @cfg {Boolean} isOriginLeft = left align?
32072      */   
32073     isOriginLeft : true,
32074     /**
32075      * @cfg {Boolean} isOriginTop = top align?
32076      */   
32077     isOriginTop : false,
32078     /**
32079      * @cfg {Boolean} isLayoutInstant = no animation?
32080      */   
32081     isLayoutInstant : false, // needed?
32082     /**
32083      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32084      */   
32085     isResizingContainer : true,
32086     /**
32087      * @cfg {Number} columnWidth  width of the columns 
32088      */   
32089     
32090     columnWidth : 0,
32091     
32092     /**
32093      * @cfg {Number} maxCols maximum number of columns
32094      */   
32095     
32096     maxCols: 0,
32097     /**
32098      * @cfg {Number} padHeight padding below box..
32099      */   
32100     
32101     padHeight : 10, 
32102     
32103     /**
32104      * @cfg {Boolean} isAutoInitial defalut true
32105      */   
32106     
32107     isAutoInitial : true, 
32108     
32109     // private?
32110     gutter : 0,
32111     
32112     containerWidth: 0,
32113     initialColumnWidth : 0,
32114     currentSize : null,
32115     
32116     colYs : null, // array.
32117     maxY : 0,
32118     padWidth: 10,
32119     
32120     
32121     tag: 'div',
32122     cls: '',
32123     bricks: null, //CompositeElement
32124     cols : 0, // array?
32125     // element : null, // wrapped now this.el
32126     _isLayoutInited : null, 
32127     
32128     
32129     getAutoCreate : function(){
32130         
32131         var cfg = {
32132             tag: this.tag,
32133             cls: 'blog-masonary-wrapper ' + this.cls,
32134             cn : {
32135                 cls : 'mas-boxes masonary'
32136             }
32137         };
32138         
32139         return cfg;
32140     },
32141     
32142     getChildContainer: function( )
32143     {
32144         if (this.boxesEl) {
32145             return this.boxesEl;
32146         }
32147         
32148         this.boxesEl = this.el.select('.mas-boxes').first();
32149         
32150         return this.boxesEl;
32151     },
32152     
32153     
32154     initEvents : function()
32155     {
32156         var _this = this;
32157         
32158         if(this.isAutoInitial){
32159             Roo.log('hook children rendered');
32160             this.on('childrenrendered', function() {
32161                 Roo.log('children rendered');
32162                 _this.initial();
32163             } ,this);
32164         }
32165         
32166     },
32167     
32168     initial : function()
32169     {
32170         this.reloadItems();
32171
32172         this.currentSize = this.el.getBox(true);
32173
32174         /// was window resize... - let's see if this works..
32175         Roo.EventManager.onWindowResize(this.resize, this); 
32176
32177         if(!this.isAutoInitial){
32178             this.layout();
32179             return;
32180         }
32181         
32182         this.layout.defer(500,this);
32183     },
32184     
32185     reloadItems: function()
32186     {
32187         this.bricks = this.el.select('.masonry-brick', true);
32188         
32189         this.bricks.each(function(b) {
32190             //Roo.log(b.getSize());
32191             if (!b.attr('originalwidth')) {
32192                 b.attr('originalwidth',  b.getSize().width);
32193             }
32194             
32195         });
32196         
32197         Roo.log(this.bricks.elements.length);
32198     },
32199     
32200     resize : function()
32201     {
32202         Roo.log('resize');
32203         var cs = this.el.getBox(true);
32204         
32205         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32206             Roo.log("no change in with or X");
32207             return;
32208         }
32209         this.currentSize = cs;
32210         this.layout();
32211     },
32212     
32213     layout : function()
32214     {
32215          Roo.log('layout');
32216         this._resetLayout();
32217         //this._manageStamps();
32218       
32219         // don't animate first layout
32220         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32221         this.layoutItems( isInstant );
32222       
32223         // flag for initalized
32224         this._isLayoutInited = true;
32225     },
32226     
32227     layoutItems : function( isInstant )
32228     {
32229         //var items = this._getItemsForLayout( this.items );
32230         // original code supports filtering layout items.. we just ignore it..
32231         
32232         this._layoutItems( this.bricks , isInstant );
32233       
32234         this._postLayout();
32235     },
32236     _layoutItems : function ( items , isInstant)
32237     {
32238        //this.fireEvent( 'layout', this, items );
32239     
32240
32241         if ( !items || !items.elements.length ) {
32242           // no items, emit event with empty array
32243             return;
32244         }
32245
32246         var queue = [];
32247         items.each(function(item) {
32248             Roo.log("layout item");
32249             Roo.log(item);
32250             // get x/y object from method
32251             var position = this._getItemLayoutPosition( item );
32252             // enqueue
32253             position.item = item;
32254             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32255             queue.push( position );
32256         }, this);
32257       
32258         this._processLayoutQueue( queue );
32259     },
32260     /** Sets position of item in DOM
32261     * @param {Element} item
32262     * @param {Number} x - horizontal position
32263     * @param {Number} y - vertical position
32264     * @param {Boolean} isInstant - disables transitions
32265     */
32266     _processLayoutQueue : function( queue )
32267     {
32268         for ( var i=0, len = queue.length; i < len; i++ ) {
32269             var obj = queue[i];
32270             obj.item.position('absolute');
32271             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32272         }
32273     },
32274       
32275     
32276     /**
32277     * Any logic you want to do after each layout,
32278     * i.e. size the container
32279     */
32280     _postLayout : function()
32281     {
32282         this.resizeContainer();
32283     },
32284     
32285     resizeContainer : function()
32286     {
32287         if ( !this.isResizingContainer ) {
32288             return;
32289         }
32290         var size = this._getContainerSize();
32291         if ( size ) {
32292             this.el.setSize(size.width,size.height);
32293             this.boxesEl.setSize(size.width,size.height);
32294         }
32295     },
32296     
32297     
32298     
32299     _resetLayout : function()
32300     {
32301         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32302         this.colWidth = this.el.getWidth();
32303         //this.gutter = this.el.getWidth(); 
32304         
32305         this.measureColumns();
32306
32307         // reset column Y
32308         var i = this.cols;
32309         this.colYs = [];
32310         while (i--) {
32311             this.colYs.push( 0 );
32312         }
32313     
32314         this.maxY = 0;
32315     },
32316
32317     measureColumns : function()
32318     {
32319         this.getContainerWidth();
32320       // if columnWidth is 0, default to outerWidth of first item
32321         if ( !this.columnWidth ) {
32322             var firstItem = this.bricks.first();
32323             Roo.log(firstItem);
32324             this.columnWidth  = this.containerWidth;
32325             if (firstItem && firstItem.attr('originalwidth') ) {
32326                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32327             }
32328             // columnWidth fall back to item of first element
32329             Roo.log("set column width?");
32330                         this.initialColumnWidth = this.columnWidth  ;
32331
32332             // if first elem has no width, default to size of container
32333             
32334         }
32335         
32336         
32337         if (this.initialColumnWidth) {
32338             this.columnWidth = this.initialColumnWidth;
32339         }
32340         
32341         
32342             
32343         // column width is fixed at the top - however if container width get's smaller we should
32344         // reduce it...
32345         
32346         // this bit calcs how man columns..
32347             
32348         var columnWidth = this.columnWidth += this.gutter;
32349       
32350         // calculate columns
32351         var containerWidth = this.containerWidth + this.gutter;
32352         
32353         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32354         // fix rounding errors, typically with gutters
32355         var excess = columnWidth - containerWidth % columnWidth;
32356         
32357         
32358         // if overshoot is less than a pixel, round up, otherwise floor it
32359         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32360         cols = Math[ mathMethod ]( cols );
32361         this.cols = Math.max( cols, 1 );
32362         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32363         
32364          // padding positioning..
32365         var totalColWidth = this.cols * this.columnWidth;
32366         var padavail = this.containerWidth - totalColWidth;
32367         // so for 2 columns - we need 3 'pads'
32368         
32369         var padNeeded = (1+this.cols) * this.padWidth;
32370         
32371         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32372         
32373         this.columnWidth += padExtra
32374         //this.padWidth = Math.floor(padavail /  ( this.cols));
32375         
32376         // adjust colum width so that padding is fixed??
32377         
32378         // we have 3 columns ... total = width * 3
32379         // we have X left over... that should be used by 
32380         
32381         //if (this.expandC) {
32382             
32383         //}
32384         
32385         
32386         
32387     },
32388     
32389     getContainerWidth : function()
32390     {
32391        /* // container is parent if fit width
32392         var container = this.isFitWidth ? this.element.parentNode : this.element;
32393         // check that this.size and size are there
32394         // IE8 triggers resize on body size change, so they might not be
32395         
32396         var size = getSize( container );  //FIXME
32397         this.containerWidth = size && size.innerWidth; //FIXME
32398         */
32399          
32400         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32401         
32402     },
32403     
32404     _getItemLayoutPosition : function( item )  // what is item?
32405     {
32406         // we resize the item to our columnWidth..
32407       
32408         item.setWidth(this.columnWidth);
32409         item.autoBoxAdjust  = false;
32410         
32411         var sz = item.getSize();
32412  
32413         // how many columns does this brick span
32414         var remainder = this.containerWidth % this.columnWidth;
32415         
32416         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32417         // round if off by 1 pixel, otherwise use ceil
32418         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32419         colSpan = Math.min( colSpan, this.cols );
32420         
32421         // normally this should be '1' as we dont' currently allow multi width columns..
32422         
32423         var colGroup = this._getColGroup( colSpan );
32424         // get the minimum Y value from the columns
32425         var minimumY = Math.min.apply( Math, colGroup );
32426         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32427         
32428         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32429          
32430         // position the brick
32431         var position = {
32432             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32433             y: this.currentSize.y + minimumY + this.padHeight
32434         };
32435         
32436         Roo.log(position);
32437         // apply setHeight to necessary columns
32438         var setHeight = minimumY + sz.height + this.padHeight;
32439         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32440         
32441         var setSpan = this.cols + 1 - colGroup.length;
32442         for ( var i = 0; i < setSpan; i++ ) {
32443           this.colYs[ shortColIndex + i ] = setHeight ;
32444         }
32445       
32446         return position;
32447     },
32448     
32449     /**
32450      * @param {Number} colSpan - number of columns the element spans
32451      * @returns {Array} colGroup
32452      */
32453     _getColGroup : function( colSpan )
32454     {
32455         if ( colSpan < 2 ) {
32456           // if brick spans only one column, use all the column Ys
32457           return this.colYs;
32458         }
32459       
32460         var colGroup = [];
32461         // how many different places could this brick fit horizontally
32462         var groupCount = this.cols + 1 - colSpan;
32463         // for each group potential horizontal position
32464         for ( var i = 0; i < groupCount; i++ ) {
32465           // make an array of colY values for that one group
32466           var groupColYs = this.colYs.slice( i, i + colSpan );
32467           // and get the max value of the array
32468           colGroup[i] = Math.max.apply( Math, groupColYs );
32469         }
32470         return colGroup;
32471     },
32472     /*
32473     _manageStamp : function( stamp )
32474     {
32475         var stampSize =  stamp.getSize();
32476         var offset = stamp.getBox();
32477         // get the columns that this stamp affects
32478         var firstX = this.isOriginLeft ? offset.x : offset.right;
32479         var lastX = firstX + stampSize.width;
32480         var firstCol = Math.floor( firstX / this.columnWidth );
32481         firstCol = Math.max( 0, firstCol );
32482         
32483         var lastCol = Math.floor( lastX / this.columnWidth );
32484         // lastCol should not go over if multiple of columnWidth #425
32485         lastCol -= lastX % this.columnWidth ? 0 : 1;
32486         lastCol = Math.min( this.cols - 1, lastCol );
32487         
32488         // set colYs to bottom of the stamp
32489         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32490             stampSize.height;
32491             
32492         for ( var i = firstCol; i <= lastCol; i++ ) {
32493           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32494         }
32495     },
32496     */
32497     
32498     _getContainerSize : function()
32499     {
32500         this.maxY = Math.max.apply( Math, this.colYs );
32501         var size = {
32502             height: this.maxY
32503         };
32504       
32505         if ( this.isFitWidth ) {
32506             size.width = this._getContainerFitWidth();
32507         }
32508       
32509         return size;
32510     },
32511     
32512     _getContainerFitWidth : function()
32513     {
32514         var unusedCols = 0;
32515         // count unused columns
32516         var i = this.cols;
32517         while ( --i ) {
32518           if ( this.colYs[i] !== 0 ) {
32519             break;
32520           }
32521           unusedCols++;
32522         }
32523         // fit container to columns that have been used
32524         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32525     },
32526     
32527     needsResizeLayout : function()
32528     {
32529         var previousWidth = this.containerWidth;
32530         this.getContainerWidth();
32531         return previousWidth !== this.containerWidth;
32532     }
32533  
32534 });
32535
32536  
32537
32538  /*
32539  * - LGPL
32540  *
32541  * element
32542  * 
32543  */
32544
32545 /**
32546  * @class Roo.bootstrap.MasonryBrick
32547  * @extends Roo.bootstrap.Component
32548  * Bootstrap MasonryBrick class
32549  * 
32550  * @constructor
32551  * Create a new MasonryBrick
32552  * @param {Object} config The config object
32553  */
32554
32555 Roo.bootstrap.MasonryBrick = function(config){
32556     
32557     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32558     
32559     Roo.bootstrap.MasonryBrick.register(this);
32560     
32561     this.addEvents({
32562         // raw events
32563         /**
32564          * @event click
32565          * When a MasonryBrick is clcik
32566          * @param {Roo.bootstrap.MasonryBrick} this
32567          * @param {Roo.EventObject} e
32568          */
32569         "click" : true
32570     });
32571 };
32572
32573 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32574     
32575     /**
32576      * @cfg {String} title
32577      */   
32578     title : '',
32579     /**
32580      * @cfg {String} html
32581      */   
32582     html : '',
32583     /**
32584      * @cfg {String} bgimage
32585      */   
32586     bgimage : '',
32587     /**
32588      * @cfg {String} videourl
32589      */   
32590     videourl : '',
32591     /**
32592      * @cfg {String} cls
32593      */   
32594     cls : '',
32595     /**
32596      * @cfg {String} href
32597      */   
32598     href : '',
32599     /**
32600      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32601      */   
32602     size : 'xs',
32603     
32604     /**
32605      * @cfg {String} placetitle (center|bottom)
32606      */   
32607     placetitle : '',
32608     
32609     /**
32610      * @cfg {Boolean} isFitContainer defalut true
32611      */   
32612     isFitContainer : true, 
32613     
32614     /**
32615      * @cfg {Boolean} preventDefault defalut false
32616      */   
32617     preventDefault : false, 
32618     
32619     /**
32620      * @cfg {Boolean} inverse defalut false
32621      */   
32622     maskInverse : false, 
32623     
32624     getAutoCreate : function()
32625     {
32626         if(!this.isFitContainer){
32627             return this.getSplitAutoCreate();
32628         }
32629         
32630         var cls = 'masonry-brick masonry-brick-full';
32631         
32632         if(this.href.length){
32633             cls += ' masonry-brick-link';
32634         }
32635         
32636         if(this.bgimage.length){
32637             cls += ' masonry-brick-image';
32638         }
32639         
32640         if(this.maskInverse){
32641             cls += ' mask-inverse';
32642         }
32643         
32644         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32645             cls += ' enable-mask';
32646         }
32647         
32648         if(this.size){
32649             cls += ' masonry-' + this.size + '-brick';
32650         }
32651         
32652         if(this.placetitle.length){
32653             
32654             switch (this.placetitle) {
32655                 case 'center' :
32656                     cls += ' masonry-center-title';
32657                     break;
32658                 case 'bottom' :
32659                     cls += ' masonry-bottom-title';
32660                     break;
32661                 default:
32662                     break;
32663             }
32664             
32665         } else {
32666             if(!this.html.length && !this.bgimage.length){
32667                 cls += ' masonry-center-title';
32668             }
32669
32670             if(!this.html.length && this.bgimage.length){
32671                 cls += ' masonry-bottom-title';
32672             }
32673         }
32674         
32675         if(this.cls){
32676             cls += ' ' + this.cls;
32677         }
32678         
32679         var cfg = {
32680             tag: (this.href.length) ? 'a' : 'div',
32681             cls: cls,
32682             cn: [
32683                 {
32684                     tag: 'div',
32685                     cls: 'masonry-brick-mask'
32686                 },
32687                 {
32688                     tag: 'div',
32689                     cls: 'masonry-brick-paragraph',
32690                     cn: []
32691                 }
32692             ]
32693         };
32694         
32695         if(this.href.length){
32696             cfg.href = this.href;
32697         }
32698         
32699         var cn = cfg.cn[1].cn;
32700         
32701         if(this.title.length){
32702             cn.push({
32703                 tag: 'h4',
32704                 cls: 'masonry-brick-title',
32705                 html: this.title
32706             });
32707         }
32708         
32709         if(this.html.length){
32710             cn.push({
32711                 tag: 'p',
32712                 cls: 'masonry-brick-text',
32713                 html: this.html
32714             });
32715         }
32716         
32717         if (!this.title.length && !this.html.length) {
32718             cfg.cn[1].cls += ' hide';
32719         }
32720         
32721         if(this.bgimage.length){
32722             cfg.cn.push({
32723                 tag: 'img',
32724                 cls: 'masonry-brick-image-view',
32725                 src: this.bgimage
32726             });
32727         }
32728         
32729         if(this.videourl.length){
32730             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32731             // youtube support only?
32732             cfg.cn.push({
32733                 tag: 'iframe',
32734                 cls: 'masonry-brick-image-view',
32735                 src: vurl,
32736                 frameborder : 0,
32737                 allowfullscreen : true
32738             });
32739         }
32740         
32741         return cfg;
32742         
32743     },
32744     
32745     getSplitAutoCreate : function()
32746     {
32747         var cls = 'masonry-brick masonry-brick-split';
32748         
32749         if(this.href.length){
32750             cls += ' masonry-brick-link';
32751         }
32752         
32753         if(this.bgimage.length){
32754             cls += ' masonry-brick-image';
32755         }
32756         
32757         if(this.size){
32758             cls += ' masonry-' + this.size + '-brick';
32759         }
32760         
32761         switch (this.placetitle) {
32762             case 'center' :
32763                 cls += ' masonry-center-title';
32764                 break;
32765             case 'bottom' :
32766                 cls += ' masonry-bottom-title';
32767                 break;
32768             default:
32769                 if(!this.bgimage.length){
32770                     cls += ' masonry-center-title';
32771                 }
32772
32773                 if(this.bgimage.length){
32774                     cls += ' masonry-bottom-title';
32775                 }
32776                 break;
32777         }
32778         
32779         if(this.cls){
32780             cls += ' ' + this.cls;
32781         }
32782         
32783         var cfg = {
32784             tag: (this.href.length) ? 'a' : 'div',
32785             cls: cls,
32786             cn: [
32787                 {
32788                     tag: 'div',
32789                     cls: 'masonry-brick-split-head',
32790                     cn: [
32791                         {
32792                             tag: 'div',
32793                             cls: 'masonry-brick-paragraph',
32794                             cn: []
32795                         }
32796                     ]
32797                 },
32798                 {
32799                     tag: 'div',
32800                     cls: 'masonry-brick-split-body',
32801                     cn: []
32802                 }
32803             ]
32804         };
32805         
32806         if(this.href.length){
32807             cfg.href = this.href;
32808         }
32809         
32810         if(this.title.length){
32811             cfg.cn[0].cn[0].cn.push({
32812                 tag: 'h4',
32813                 cls: 'masonry-brick-title',
32814                 html: this.title
32815             });
32816         }
32817         
32818         if(this.html.length){
32819             cfg.cn[1].cn.push({
32820                 tag: 'p',
32821                 cls: 'masonry-brick-text',
32822                 html: this.html
32823             });
32824         }
32825
32826         if(this.bgimage.length){
32827             cfg.cn[0].cn.push({
32828                 tag: 'img',
32829                 cls: 'masonry-brick-image-view',
32830                 src: this.bgimage
32831             });
32832         }
32833         
32834         if(this.videourl.length){
32835             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32836             // youtube support only?
32837             cfg.cn[0].cn.cn.push({
32838                 tag: 'iframe',
32839                 cls: 'masonry-brick-image-view',
32840                 src: vurl,
32841                 frameborder : 0,
32842                 allowfullscreen : true
32843             });
32844         }
32845         
32846         return cfg;
32847     },
32848     
32849     initEvents: function() 
32850     {
32851         switch (this.size) {
32852             case 'xs' :
32853                 this.x = 1;
32854                 this.y = 1;
32855                 break;
32856             case 'sm' :
32857                 this.x = 2;
32858                 this.y = 2;
32859                 break;
32860             case 'md' :
32861             case 'md-left' :
32862             case 'md-right' :
32863                 this.x = 3;
32864                 this.y = 3;
32865                 break;
32866             case 'tall' :
32867                 this.x = 2;
32868                 this.y = 3;
32869                 break;
32870             case 'wide' :
32871                 this.x = 3;
32872                 this.y = 2;
32873                 break;
32874             case 'wide-thin' :
32875                 this.x = 3;
32876                 this.y = 1;
32877                 break;
32878                         
32879             default :
32880                 break;
32881         }
32882         
32883         if(Roo.isTouch){
32884             this.el.on('touchstart', this.onTouchStart, this);
32885             this.el.on('touchmove', this.onTouchMove, this);
32886             this.el.on('touchend', this.onTouchEnd, this);
32887             this.el.on('contextmenu', this.onContextMenu, this);
32888         } else {
32889             this.el.on('mouseenter'  ,this.enter, this);
32890             this.el.on('mouseleave', this.leave, this);
32891             this.el.on('click', this.onClick, this);
32892         }
32893         
32894         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32895             this.parent().bricks.push(this);   
32896         }
32897         
32898     },
32899     
32900     onClick: function(e, el)
32901     {
32902         var time = this.endTimer - this.startTimer;
32903         // Roo.log(e.preventDefault());
32904         if(Roo.isTouch){
32905             if(time > 1000){
32906                 e.preventDefault();
32907                 return;
32908             }
32909         }
32910         
32911         if(!this.preventDefault){
32912             return;
32913         }
32914         
32915         e.preventDefault();
32916         
32917         if (this.activeClass != '') {
32918             this.selectBrick();
32919         }
32920         
32921         this.fireEvent('click', this, e);
32922     },
32923     
32924     enter: function(e, el)
32925     {
32926         e.preventDefault();
32927         
32928         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32929             return;
32930         }
32931         
32932         if(this.bgimage.length && this.html.length){
32933             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32934         }
32935     },
32936     
32937     leave: function(e, el)
32938     {
32939         e.preventDefault();
32940         
32941         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32942             return;
32943         }
32944         
32945         if(this.bgimage.length && this.html.length){
32946             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32947         }
32948     },
32949     
32950     onTouchStart: function(e, el)
32951     {
32952 //        e.preventDefault();
32953         
32954         this.touchmoved = false;
32955         
32956         if(!this.isFitContainer){
32957             return;
32958         }
32959         
32960         if(!this.bgimage.length || !this.html.length){
32961             return;
32962         }
32963         
32964         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32965         
32966         this.timer = new Date().getTime();
32967         
32968     },
32969     
32970     onTouchMove: function(e, el)
32971     {
32972         this.touchmoved = true;
32973     },
32974     
32975     onContextMenu : function(e,el)
32976     {
32977         e.preventDefault();
32978         e.stopPropagation();
32979         return false;
32980     },
32981     
32982     onTouchEnd: function(e, el)
32983     {
32984 //        e.preventDefault();
32985         
32986         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32987         
32988             this.leave(e,el);
32989             
32990             return;
32991         }
32992         
32993         if(!this.bgimage.length || !this.html.length){
32994             
32995             if(this.href.length){
32996                 window.location.href = this.href;
32997             }
32998             
32999             return;
33000         }
33001         
33002         if(!this.isFitContainer){
33003             return;
33004         }
33005         
33006         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33007         
33008         window.location.href = this.href;
33009     },
33010     
33011     //selection on single brick only
33012     selectBrick : function() {
33013         
33014         if (!this.parentId) {
33015             return;
33016         }
33017         
33018         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33019         var index = m.selectedBrick.indexOf(this.id);
33020         
33021         if ( index > -1) {
33022             m.selectedBrick.splice(index,1);
33023             this.el.removeClass(this.activeClass);
33024             return;
33025         }
33026         
33027         for(var i = 0; i < m.selectedBrick.length; i++) {
33028             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33029             b.el.removeClass(b.activeClass);
33030         }
33031         
33032         m.selectedBrick = [];
33033         
33034         m.selectedBrick.push(this.id);
33035         this.el.addClass(this.activeClass);
33036         return;
33037     },
33038     
33039     isSelected : function(){
33040         return this.el.hasClass(this.activeClass);
33041         
33042     }
33043 });
33044
33045 Roo.apply(Roo.bootstrap.MasonryBrick, {
33046     
33047     //groups: {},
33048     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33049      /**
33050     * register a Masonry Brick
33051     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33052     */
33053     
33054     register : function(brick)
33055     {
33056         //this.groups[brick.id] = brick;
33057         this.groups.add(brick.id, brick);
33058     },
33059     /**
33060     * fetch a  masonry brick based on the masonry brick ID
33061     * @param {string} the masonry brick to add
33062     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33063     */
33064     
33065     get: function(brick_id) 
33066     {
33067         // if (typeof(this.groups[brick_id]) == 'undefined') {
33068         //     return false;
33069         // }
33070         // return this.groups[brick_id] ;
33071         
33072         if(this.groups.key(brick_id)) {
33073             return this.groups.key(brick_id);
33074         }
33075         
33076         return false;
33077     }
33078     
33079     
33080     
33081 });
33082
33083  /*
33084  * - LGPL
33085  *
33086  * element
33087  * 
33088  */
33089
33090 /**
33091  * @class Roo.bootstrap.Brick
33092  * @extends Roo.bootstrap.Component
33093  * Bootstrap Brick class
33094  * 
33095  * @constructor
33096  * Create a new Brick
33097  * @param {Object} config The config object
33098  */
33099
33100 Roo.bootstrap.Brick = function(config){
33101     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33102     
33103     this.addEvents({
33104         // raw events
33105         /**
33106          * @event click
33107          * When a Brick is click
33108          * @param {Roo.bootstrap.Brick} this
33109          * @param {Roo.EventObject} e
33110          */
33111         "click" : true
33112     });
33113 };
33114
33115 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33116     
33117     /**
33118      * @cfg {String} title
33119      */   
33120     title : '',
33121     /**
33122      * @cfg {String} html
33123      */   
33124     html : '',
33125     /**
33126      * @cfg {String} bgimage
33127      */   
33128     bgimage : '',
33129     /**
33130      * @cfg {String} cls
33131      */   
33132     cls : '',
33133     /**
33134      * @cfg {String} href
33135      */   
33136     href : '',
33137     /**
33138      * @cfg {String} video
33139      */   
33140     video : '',
33141     /**
33142      * @cfg {Boolean} square
33143      */   
33144     square : true,
33145     
33146     getAutoCreate : function()
33147     {
33148         var cls = 'roo-brick';
33149         
33150         if(this.href.length){
33151             cls += ' roo-brick-link';
33152         }
33153         
33154         if(this.bgimage.length){
33155             cls += ' roo-brick-image';
33156         }
33157         
33158         if(!this.html.length && !this.bgimage.length){
33159             cls += ' roo-brick-center-title';
33160         }
33161         
33162         if(!this.html.length && this.bgimage.length){
33163             cls += ' roo-brick-bottom-title';
33164         }
33165         
33166         if(this.cls){
33167             cls += ' ' + this.cls;
33168         }
33169         
33170         var cfg = {
33171             tag: (this.href.length) ? 'a' : 'div',
33172             cls: cls,
33173             cn: [
33174                 {
33175                     tag: 'div',
33176                     cls: 'roo-brick-paragraph',
33177                     cn: []
33178                 }
33179             ]
33180         };
33181         
33182         if(this.href.length){
33183             cfg.href = this.href;
33184         }
33185         
33186         var cn = cfg.cn[0].cn;
33187         
33188         if(this.title.length){
33189             cn.push({
33190                 tag: 'h4',
33191                 cls: 'roo-brick-title',
33192                 html: this.title
33193             });
33194         }
33195         
33196         if(this.html.length){
33197             cn.push({
33198                 tag: 'p',
33199                 cls: 'roo-brick-text',
33200                 html: this.html
33201             });
33202         } else {
33203             cn.cls += ' hide';
33204         }
33205         
33206         if(this.bgimage.length){
33207             cfg.cn.push({
33208                 tag: 'img',
33209                 cls: 'roo-brick-image-view',
33210                 src: this.bgimage
33211             });
33212         }
33213         
33214         return cfg;
33215     },
33216     
33217     initEvents: function() 
33218     {
33219         if(this.title.length || this.html.length){
33220             this.el.on('mouseenter'  ,this.enter, this);
33221             this.el.on('mouseleave', this.leave, this);
33222         }
33223         
33224         Roo.EventManager.onWindowResize(this.resize, this); 
33225         
33226         if(this.bgimage.length){
33227             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33228             this.imageEl.on('load', this.onImageLoad, this);
33229             return;
33230         }
33231         
33232         this.resize();
33233     },
33234     
33235     onImageLoad : function()
33236     {
33237         this.resize();
33238     },
33239     
33240     resize : function()
33241     {
33242         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33243         
33244         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33245         
33246         if(this.bgimage.length){
33247             var image = this.el.select('.roo-brick-image-view', true).first();
33248             
33249             image.setWidth(paragraph.getWidth());
33250             
33251             if(this.square){
33252                 image.setHeight(paragraph.getWidth());
33253             }
33254             
33255             this.el.setHeight(image.getHeight());
33256             paragraph.setHeight(image.getHeight());
33257             
33258         }
33259         
33260     },
33261     
33262     enter: function(e, el)
33263     {
33264         e.preventDefault();
33265         
33266         if(this.bgimage.length){
33267             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33268             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33269         }
33270     },
33271     
33272     leave: function(e, el)
33273     {
33274         e.preventDefault();
33275         
33276         if(this.bgimage.length){
33277             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33278             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33279         }
33280     }
33281     
33282 });
33283
33284  
33285
33286  /*
33287  * - LGPL
33288  *
33289  * Number field 
33290  */
33291
33292 /**
33293  * @class Roo.bootstrap.NumberField
33294  * @extends Roo.bootstrap.Input
33295  * Bootstrap NumberField class
33296  * 
33297  * 
33298  * 
33299  * 
33300  * @constructor
33301  * Create a new NumberField
33302  * @param {Object} config The config object
33303  */
33304
33305 Roo.bootstrap.NumberField = function(config){
33306     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33307 };
33308
33309 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33310     
33311     /**
33312      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33313      */
33314     allowDecimals : true,
33315     /**
33316      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33317      */
33318     decimalSeparator : ".",
33319     /**
33320      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33321      */
33322     decimalPrecision : 2,
33323     /**
33324      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33325      */
33326     allowNegative : true,
33327     
33328     /**
33329      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33330      */
33331     allowZero: true,
33332     /**
33333      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33334      */
33335     minValue : Number.NEGATIVE_INFINITY,
33336     /**
33337      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33338      */
33339     maxValue : Number.MAX_VALUE,
33340     /**
33341      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33342      */
33343     minText : "The minimum value for this field is {0}",
33344     /**
33345      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33346      */
33347     maxText : "The maximum value for this field is {0}",
33348     /**
33349      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33350      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33351      */
33352     nanText : "{0} is not a valid number",
33353     /**
33354      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33355      */
33356     thousandsDelimiter : false,
33357     /**
33358      * @cfg {String} valueAlign alignment of value
33359      */
33360     valueAlign : "left",
33361
33362     getAutoCreate : function()
33363     {
33364         var hiddenInput = {
33365             tag: 'input',
33366             type: 'hidden',
33367             id: Roo.id(),
33368             cls: 'hidden-number-input'
33369         };
33370         
33371         if (this.name) {
33372             hiddenInput.name = this.name;
33373         }
33374         
33375         this.name = '';
33376         
33377         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33378         
33379         this.name = hiddenInput.name;
33380         
33381         if(cfg.cn.length > 0) {
33382             cfg.cn.push(hiddenInput);
33383         }
33384         
33385         return cfg;
33386     },
33387
33388     // private
33389     initEvents : function()
33390     {   
33391         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33392         
33393         var allowed = "0123456789";
33394         
33395         if(this.allowDecimals){
33396             allowed += this.decimalSeparator;
33397         }
33398         
33399         if(this.allowNegative){
33400             allowed += "-";
33401         }
33402         
33403         if(this.thousandsDelimiter) {
33404             allowed += ",";
33405         }
33406         
33407         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33408         
33409         var keyPress = function(e){
33410             
33411             var k = e.getKey();
33412             
33413             var c = e.getCharCode();
33414             
33415             if(
33416                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33417                     allowed.indexOf(String.fromCharCode(c)) === -1
33418             ){
33419                 e.stopEvent();
33420                 return;
33421             }
33422             
33423             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33424                 return;
33425             }
33426             
33427             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33428                 e.stopEvent();
33429             }
33430         };
33431         
33432         this.el.on("keypress", keyPress, this);
33433     },
33434     
33435     validateValue : function(value)
33436     {
33437         
33438         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33439             return false;
33440         }
33441         
33442         var num = this.parseValue(value);
33443         
33444         if(isNaN(num)){
33445             this.markInvalid(String.format(this.nanText, value));
33446             return false;
33447         }
33448         
33449         if(num < this.minValue){
33450             this.markInvalid(String.format(this.minText, this.minValue));
33451             return false;
33452         }
33453         
33454         if(num > this.maxValue){
33455             this.markInvalid(String.format(this.maxText, this.maxValue));
33456             return false;
33457         }
33458         
33459         return true;
33460     },
33461
33462     getValue : function()
33463     {
33464         var v = this.hiddenEl().getValue();
33465         
33466         return this.fixPrecision(this.parseValue(v));
33467     },
33468
33469     parseValue : function(value)
33470     {
33471         if(this.thousandsDelimiter) {
33472             value += "";
33473             r = new RegExp(",", "g");
33474             value = value.replace(r, "");
33475         }
33476         
33477         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33478         return isNaN(value) ? '' : value;
33479     },
33480
33481     fixPrecision : function(value)
33482     {
33483         if(this.thousandsDelimiter) {
33484             value += "";
33485             r = new RegExp(",", "g");
33486             value = value.replace(r, "");
33487         }
33488         
33489         var nan = isNaN(value);
33490         
33491         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33492             return nan ? '' : value;
33493         }
33494         return parseFloat(value).toFixed(this.decimalPrecision);
33495     },
33496
33497     setValue : function(v)
33498     {
33499         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33500         
33501         this.value = v;
33502         
33503         if(this.rendered){
33504             
33505             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33506             
33507             this.inputEl().dom.value = (v == '') ? '' :
33508                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33509             
33510             if(!this.allowZero && v === '0') {
33511                 this.hiddenEl().dom.value = '';
33512                 this.inputEl().dom.value = '';
33513             }
33514             
33515             this.validate();
33516         }
33517     },
33518
33519     decimalPrecisionFcn : function(v)
33520     {
33521         return Math.floor(v);
33522     },
33523
33524     beforeBlur : function()
33525     {
33526         var v = this.parseValue(this.getRawValue());
33527         
33528         if(v || v === 0 || v === ''){
33529             this.setValue(v);
33530         }
33531     },
33532     
33533     hiddenEl : function()
33534     {
33535         return this.el.select('input.hidden-number-input',true).first();
33536     }
33537     
33538 });
33539
33540  
33541
33542 /*
33543 * Licence: LGPL
33544 */
33545
33546 /**
33547  * @class Roo.bootstrap.DocumentSlider
33548  * @extends Roo.bootstrap.Component
33549  * Bootstrap DocumentSlider class
33550  * 
33551  * @constructor
33552  * Create a new DocumentViewer
33553  * @param {Object} config The config object
33554  */
33555
33556 Roo.bootstrap.DocumentSlider = function(config){
33557     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33558     
33559     this.files = [];
33560     
33561     this.addEvents({
33562         /**
33563          * @event initial
33564          * Fire after initEvent
33565          * @param {Roo.bootstrap.DocumentSlider} this
33566          */
33567         "initial" : true,
33568         /**
33569          * @event update
33570          * Fire after update
33571          * @param {Roo.bootstrap.DocumentSlider} this
33572          */
33573         "update" : true,
33574         /**
33575          * @event click
33576          * Fire after click
33577          * @param {Roo.bootstrap.DocumentSlider} this
33578          */
33579         "click" : true
33580     });
33581 };
33582
33583 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33584     
33585     files : false,
33586     
33587     indicator : 0,
33588     
33589     getAutoCreate : function()
33590     {
33591         var cfg = {
33592             tag : 'div',
33593             cls : 'roo-document-slider',
33594             cn : [
33595                 {
33596                     tag : 'div',
33597                     cls : 'roo-document-slider-header',
33598                     cn : [
33599                         {
33600                             tag : 'div',
33601                             cls : 'roo-document-slider-header-title'
33602                         }
33603                     ]
33604                 },
33605                 {
33606                     tag : 'div',
33607                     cls : 'roo-document-slider-body',
33608                     cn : [
33609                         {
33610                             tag : 'div',
33611                             cls : 'roo-document-slider-prev',
33612                             cn : [
33613                                 {
33614                                     tag : 'i',
33615                                     cls : 'fa fa-chevron-left'
33616                                 }
33617                             ]
33618                         },
33619                         {
33620                             tag : 'div',
33621                             cls : 'roo-document-slider-thumb',
33622                             cn : [
33623                                 {
33624                                     tag : 'img',
33625                                     cls : 'roo-document-slider-image'
33626                                 }
33627                             ]
33628                         },
33629                         {
33630                             tag : 'div',
33631                             cls : 'roo-document-slider-next',
33632                             cn : [
33633                                 {
33634                                     tag : 'i',
33635                                     cls : 'fa fa-chevron-right'
33636                                 }
33637                             ]
33638                         }
33639                     ]
33640                 }
33641             ]
33642         };
33643         
33644         return cfg;
33645     },
33646     
33647     initEvents : function()
33648     {
33649         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33650         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33651         
33652         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33653         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33654         
33655         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33656         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33657         
33658         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33659         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33660         
33661         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33662         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33663         
33664         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33665         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33666         
33667         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33668         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33669         
33670         this.thumbEl.on('click', this.onClick, this);
33671         
33672         this.prevIndicator.on('click', this.prev, this);
33673         
33674         this.nextIndicator.on('click', this.next, this);
33675         
33676     },
33677     
33678     initial : function()
33679     {
33680         if(this.files.length){
33681             this.indicator = 1;
33682             this.update()
33683         }
33684         
33685         this.fireEvent('initial', this);
33686     },
33687     
33688     update : function()
33689     {
33690         this.imageEl.attr('src', this.files[this.indicator - 1]);
33691         
33692         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33693         
33694         this.prevIndicator.show();
33695         
33696         if(this.indicator == 1){
33697             this.prevIndicator.hide();
33698         }
33699         
33700         this.nextIndicator.show();
33701         
33702         if(this.indicator == this.files.length){
33703             this.nextIndicator.hide();
33704         }
33705         
33706         this.thumbEl.scrollTo('top');
33707         
33708         this.fireEvent('update', this);
33709     },
33710     
33711     onClick : function(e)
33712     {
33713         e.preventDefault();
33714         
33715         this.fireEvent('click', this);
33716     },
33717     
33718     prev : function(e)
33719     {
33720         e.preventDefault();
33721         
33722         this.indicator = Math.max(1, this.indicator - 1);
33723         
33724         this.update();
33725     },
33726     
33727     next : function(e)
33728     {
33729         e.preventDefault();
33730         
33731         this.indicator = Math.min(this.files.length, this.indicator + 1);
33732         
33733         this.update();
33734     }
33735 });
33736 /*
33737  * - LGPL
33738  *
33739  * RadioSet
33740  *
33741  *
33742  */
33743
33744 /**
33745  * @class Roo.bootstrap.RadioSet
33746  * @extends Roo.bootstrap.Input
33747  * Bootstrap RadioSet class
33748  * @cfg {String} indicatorpos (left|right) default left
33749  * @cfg {Boolean} inline (true|false) inline the element (default true)
33750  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33751  * @constructor
33752  * Create a new RadioSet
33753  * @param {Object} config The config object
33754  */
33755
33756 Roo.bootstrap.RadioSet = function(config){
33757     
33758     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33759     
33760     this.radioes = [];
33761     
33762     Roo.bootstrap.RadioSet.register(this);
33763     
33764     this.addEvents({
33765         /**
33766         * @event check
33767         * Fires when the element is checked or unchecked.
33768         * @param {Roo.bootstrap.RadioSet} this This radio
33769         * @param {Roo.bootstrap.Radio} item The checked item
33770         */
33771        check : true,
33772        /**
33773         * @event click
33774         * Fires when the element is click.
33775         * @param {Roo.bootstrap.RadioSet} this This radio set
33776         * @param {Roo.bootstrap.Radio} item The checked item
33777         * @param {Roo.EventObject} e The event object
33778         */
33779        click : true
33780     });
33781     
33782 };
33783
33784 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33785
33786     radioes : false,
33787     
33788     inline : true,
33789     
33790     weight : '',
33791     
33792     indicatorpos : 'left',
33793     
33794     getAutoCreate : function()
33795     {
33796         var label = {
33797             tag : 'label',
33798             cls : 'roo-radio-set-label',
33799             cn : [
33800                 {
33801                     tag : 'span',
33802                     html : this.fieldLabel
33803                 }
33804             ]
33805         };
33806         
33807         if(this.indicatorpos == 'left'){
33808             label.cn.unshift({
33809                 tag : 'i',
33810                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33811                 tooltip : 'This field is required'
33812             });
33813         } else {
33814             label.cn.push({
33815                 tag : 'i',
33816                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33817                 tooltip : 'This field is required'
33818             });
33819         }
33820         
33821         var items = {
33822             tag : 'div',
33823             cls : 'roo-radio-set-items'
33824         };
33825         
33826         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33827         
33828         if (align === 'left' && this.fieldLabel.length) {
33829             
33830             items = {
33831                 cls : "roo-radio-set-right", 
33832                 cn: [
33833                     items
33834                 ]
33835             };
33836             
33837             if(this.labelWidth > 12){
33838                 label.style = "width: " + this.labelWidth + 'px';
33839             }
33840             
33841             if(this.labelWidth < 13 && this.labelmd == 0){
33842                 this.labelmd = this.labelWidth;
33843             }
33844             
33845             if(this.labellg > 0){
33846                 label.cls += ' col-lg-' + this.labellg;
33847                 items.cls += ' col-lg-' + (12 - this.labellg);
33848             }
33849             
33850             if(this.labelmd > 0){
33851                 label.cls += ' col-md-' + this.labelmd;
33852                 items.cls += ' col-md-' + (12 - this.labelmd);
33853             }
33854             
33855             if(this.labelsm > 0){
33856                 label.cls += ' col-sm-' + this.labelsm;
33857                 items.cls += ' col-sm-' + (12 - this.labelsm);
33858             }
33859             
33860             if(this.labelxs > 0){
33861                 label.cls += ' col-xs-' + this.labelxs;
33862                 items.cls += ' col-xs-' + (12 - this.labelxs);
33863             }
33864         }
33865         
33866         var cfg = {
33867             tag : 'div',
33868             cls : 'roo-radio-set',
33869             cn : [
33870                 {
33871                     tag : 'input',
33872                     cls : 'roo-radio-set-input',
33873                     type : 'hidden',
33874                     name : this.name,
33875                     value : this.value ? this.value :  ''
33876                 },
33877                 label,
33878                 items
33879             ]
33880         };
33881         
33882         if(this.weight.length){
33883             cfg.cls += ' roo-radio-' + this.weight;
33884         }
33885         
33886         if(this.inline) {
33887             cfg.cls += ' roo-radio-set-inline';
33888         }
33889         
33890         var settings=this;
33891         ['xs','sm','md','lg'].map(function(size){
33892             if (settings[size]) {
33893                 cfg.cls += ' col-' + size + '-' + settings[size];
33894             }
33895         });
33896         
33897         return cfg;
33898         
33899     },
33900
33901     initEvents : function()
33902     {
33903         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33904         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33905         
33906         if(!this.fieldLabel.length){
33907             this.labelEl.hide();
33908         }
33909         
33910         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33911         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33912         
33913         this.indicator = this.indicatorEl();
33914         
33915         if(this.indicator){
33916             this.indicator.addClass('invisible');
33917         }
33918         
33919         this.originalValue = this.getValue();
33920         
33921     },
33922     
33923     inputEl: function ()
33924     {
33925         return this.el.select('.roo-radio-set-input', true).first();
33926     },
33927     
33928     getChildContainer : function()
33929     {
33930         return this.itemsEl;
33931     },
33932     
33933     register : function(item)
33934     {
33935         this.radioes.push(item);
33936         
33937     },
33938     
33939     validate : function()
33940     {   
33941         if(this.getVisibilityEl().hasClass('hidden')){
33942             return true;
33943         }
33944         
33945         var valid = false;
33946         
33947         Roo.each(this.radioes, function(i){
33948             if(!i.checked){
33949                 return;
33950             }
33951             
33952             valid = true;
33953             return false;
33954         });
33955         
33956         if(this.allowBlank) {
33957             return true;
33958         }
33959         
33960         if(this.disabled || valid){
33961             this.markValid();
33962             return true;
33963         }
33964         
33965         this.markInvalid();
33966         return false;
33967         
33968     },
33969     
33970     markValid : function()
33971     {
33972         if(this.labelEl.isVisible(true)){
33973             this.indicatorEl().removeClass('visible');
33974             this.indicatorEl().addClass('invisible');
33975         }
33976         
33977         this.el.removeClass([this.invalidClass, this.validClass]);
33978         this.el.addClass(this.validClass);
33979         
33980         this.fireEvent('valid', this);
33981     },
33982     
33983     markInvalid : function(msg)
33984     {
33985         if(this.allowBlank || this.disabled){
33986             return;
33987         }
33988         
33989         if(this.labelEl.isVisible(true)){
33990             this.indicatorEl().removeClass('invisible');
33991             this.indicatorEl().addClass('visible');
33992         }
33993         
33994         this.el.removeClass([this.invalidClass, this.validClass]);
33995         this.el.addClass(this.invalidClass);
33996         
33997         this.fireEvent('invalid', this, msg);
33998         
33999     },
34000     
34001     setValue : function(v, suppressEvent)
34002     {   
34003         if(this.value === v){
34004             return;
34005         }
34006         
34007         this.value = v;
34008         
34009         if(this.rendered){
34010             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34011         }
34012         
34013         Roo.each(this.radioes, function(i){
34014             i.checked = false;
34015             i.el.removeClass('checked');
34016         });
34017         
34018         Roo.each(this.radioes, function(i){
34019             
34020             if(i.value === v || i.value.toString() === v.toString()){
34021                 i.checked = true;
34022                 i.el.addClass('checked');
34023                 
34024                 if(suppressEvent !== true){
34025                     this.fireEvent('check', this, i);
34026                 }
34027                 
34028                 return false;
34029             }
34030             
34031         }, this);
34032         
34033         this.validate();
34034     },
34035     
34036     clearInvalid : function(){
34037         
34038         if(!this.el || this.preventMark){
34039             return;
34040         }
34041         
34042         this.el.removeClass([this.invalidClass]);
34043         
34044         this.fireEvent('valid', this);
34045     }
34046     
34047 });
34048
34049 Roo.apply(Roo.bootstrap.RadioSet, {
34050     
34051     groups: {},
34052     
34053     register : function(set)
34054     {
34055         this.groups[set.name] = set;
34056     },
34057     
34058     get: function(name) 
34059     {
34060         if (typeof(this.groups[name]) == 'undefined') {
34061             return false;
34062         }
34063         
34064         return this.groups[name] ;
34065     }
34066     
34067 });
34068 /*
34069  * Based on:
34070  * Ext JS Library 1.1.1
34071  * Copyright(c) 2006-2007, Ext JS, LLC.
34072  *
34073  * Originally Released Under LGPL - original licence link has changed is not relivant.
34074  *
34075  * Fork - LGPL
34076  * <script type="text/javascript">
34077  */
34078
34079
34080 /**
34081  * @class Roo.bootstrap.SplitBar
34082  * @extends Roo.util.Observable
34083  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34084  * <br><br>
34085  * Usage:
34086  * <pre><code>
34087 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34088                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34089 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34090 split.minSize = 100;
34091 split.maxSize = 600;
34092 split.animate = true;
34093 split.on('moved', splitterMoved);
34094 </code></pre>
34095  * @constructor
34096  * Create a new SplitBar
34097  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34098  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34099  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34100  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34101                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34102                         position of the SplitBar).
34103  */
34104 Roo.bootstrap.SplitBar = function(cfg){
34105     
34106     /** @private */
34107     
34108     //{
34109     //  dragElement : elm
34110     //  resizingElement: el,
34111         // optional..
34112     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34113     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34114         // existingProxy ???
34115     //}
34116     
34117     this.el = Roo.get(cfg.dragElement, true);
34118     this.el.dom.unselectable = "on";
34119     /** @private */
34120     this.resizingEl = Roo.get(cfg.resizingElement, true);
34121
34122     /**
34123      * @private
34124      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34125      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34126      * @type Number
34127      */
34128     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34129     
34130     /**
34131      * The minimum size of the resizing element. (Defaults to 0)
34132      * @type Number
34133      */
34134     this.minSize = 0;
34135     
34136     /**
34137      * The maximum size of the resizing element. (Defaults to 2000)
34138      * @type Number
34139      */
34140     this.maxSize = 2000;
34141     
34142     /**
34143      * Whether to animate the transition to the new size
34144      * @type Boolean
34145      */
34146     this.animate = false;
34147     
34148     /**
34149      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34150      * @type Boolean
34151      */
34152     this.useShim = false;
34153     
34154     /** @private */
34155     this.shim = null;
34156     
34157     if(!cfg.existingProxy){
34158         /** @private */
34159         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34160     }else{
34161         this.proxy = Roo.get(cfg.existingProxy).dom;
34162     }
34163     /** @private */
34164     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34165     
34166     /** @private */
34167     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34168     
34169     /** @private */
34170     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34171     
34172     /** @private */
34173     this.dragSpecs = {};
34174     
34175     /**
34176      * @private The adapter to use to positon and resize elements
34177      */
34178     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34179     this.adapter.init(this);
34180     
34181     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34182         /** @private */
34183         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34184         this.el.addClass("roo-splitbar-h");
34185     }else{
34186         /** @private */
34187         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34188         this.el.addClass("roo-splitbar-v");
34189     }
34190     
34191     this.addEvents({
34192         /**
34193          * @event resize
34194          * Fires when the splitter is moved (alias for {@link #event-moved})
34195          * @param {Roo.bootstrap.SplitBar} this
34196          * @param {Number} newSize the new width or height
34197          */
34198         "resize" : true,
34199         /**
34200          * @event moved
34201          * Fires when the splitter is moved
34202          * @param {Roo.bootstrap.SplitBar} this
34203          * @param {Number} newSize the new width or height
34204          */
34205         "moved" : true,
34206         /**
34207          * @event beforeresize
34208          * Fires before the splitter is dragged
34209          * @param {Roo.bootstrap.SplitBar} this
34210          */
34211         "beforeresize" : true,
34212
34213         "beforeapply" : true
34214     });
34215
34216     Roo.util.Observable.call(this);
34217 };
34218
34219 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34220     onStartProxyDrag : function(x, y){
34221         this.fireEvent("beforeresize", this);
34222         if(!this.overlay){
34223             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34224             o.unselectable();
34225             o.enableDisplayMode("block");
34226             // all splitbars share the same overlay
34227             Roo.bootstrap.SplitBar.prototype.overlay = o;
34228         }
34229         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34230         this.overlay.show();
34231         Roo.get(this.proxy).setDisplayed("block");
34232         var size = this.adapter.getElementSize(this);
34233         this.activeMinSize = this.getMinimumSize();;
34234         this.activeMaxSize = this.getMaximumSize();;
34235         var c1 = size - this.activeMinSize;
34236         var c2 = Math.max(this.activeMaxSize - size, 0);
34237         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34238             this.dd.resetConstraints();
34239             this.dd.setXConstraint(
34240                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34241                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34242             );
34243             this.dd.setYConstraint(0, 0);
34244         }else{
34245             this.dd.resetConstraints();
34246             this.dd.setXConstraint(0, 0);
34247             this.dd.setYConstraint(
34248                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34249                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34250             );
34251          }
34252         this.dragSpecs.startSize = size;
34253         this.dragSpecs.startPoint = [x, y];
34254         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34255     },
34256     
34257     /** 
34258      * @private Called after the drag operation by the DDProxy
34259      */
34260     onEndProxyDrag : function(e){
34261         Roo.get(this.proxy).setDisplayed(false);
34262         var endPoint = Roo.lib.Event.getXY(e);
34263         if(this.overlay){
34264             this.overlay.hide();
34265         }
34266         var newSize;
34267         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34268             newSize = this.dragSpecs.startSize + 
34269                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34270                     endPoint[0] - this.dragSpecs.startPoint[0] :
34271                     this.dragSpecs.startPoint[0] - endPoint[0]
34272                 );
34273         }else{
34274             newSize = this.dragSpecs.startSize + 
34275                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34276                     endPoint[1] - this.dragSpecs.startPoint[1] :
34277                     this.dragSpecs.startPoint[1] - endPoint[1]
34278                 );
34279         }
34280         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34281         if(newSize != this.dragSpecs.startSize){
34282             if(this.fireEvent('beforeapply', this, newSize) !== false){
34283                 this.adapter.setElementSize(this, newSize);
34284                 this.fireEvent("moved", this, newSize);
34285                 this.fireEvent("resize", this, newSize);
34286             }
34287         }
34288     },
34289     
34290     /**
34291      * Get the adapter this SplitBar uses
34292      * @return The adapter object
34293      */
34294     getAdapter : function(){
34295         return this.adapter;
34296     },
34297     
34298     /**
34299      * Set the adapter this SplitBar uses
34300      * @param {Object} adapter A SplitBar adapter object
34301      */
34302     setAdapter : function(adapter){
34303         this.adapter = adapter;
34304         this.adapter.init(this);
34305     },
34306     
34307     /**
34308      * Gets the minimum size for the resizing element
34309      * @return {Number} The minimum size
34310      */
34311     getMinimumSize : function(){
34312         return this.minSize;
34313     },
34314     
34315     /**
34316      * Sets the minimum size for the resizing element
34317      * @param {Number} minSize The minimum size
34318      */
34319     setMinimumSize : function(minSize){
34320         this.minSize = minSize;
34321     },
34322     
34323     /**
34324      * Gets the maximum size for the resizing element
34325      * @return {Number} The maximum size
34326      */
34327     getMaximumSize : function(){
34328         return this.maxSize;
34329     },
34330     
34331     /**
34332      * Sets the maximum size for the resizing element
34333      * @param {Number} maxSize The maximum size
34334      */
34335     setMaximumSize : function(maxSize){
34336         this.maxSize = maxSize;
34337     },
34338     
34339     /**
34340      * Sets the initialize size for the resizing element
34341      * @param {Number} size The initial size
34342      */
34343     setCurrentSize : function(size){
34344         var oldAnimate = this.animate;
34345         this.animate = false;
34346         this.adapter.setElementSize(this, size);
34347         this.animate = oldAnimate;
34348     },
34349     
34350     /**
34351      * Destroy this splitbar. 
34352      * @param {Boolean} removeEl True to remove the element
34353      */
34354     destroy : function(removeEl){
34355         if(this.shim){
34356             this.shim.remove();
34357         }
34358         this.dd.unreg();
34359         this.proxy.parentNode.removeChild(this.proxy);
34360         if(removeEl){
34361             this.el.remove();
34362         }
34363     }
34364 });
34365
34366 /**
34367  * @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.
34368  */
34369 Roo.bootstrap.SplitBar.createProxy = function(dir){
34370     var proxy = new Roo.Element(document.createElement("div"));
34371     proxy.unselectable();
34372     var cls = 'roo-splitbar-proxy';
34373     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34374     document.body.appendChild(proxy.dom);
34375     return proxy.dom;
34376 };
34377
34378 /** 
34379  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34380  * Default Adapter. It assumes the splitter and resizing element are not positioned
34381  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34382  */
34383 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34384 };
34385
34386 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34387     // do nothing for now
34388     init : function(s){
34389     
34390     },
34391     /**
34392      * Called before drag operations to get the current size of the resizing element. 
34393      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34394      */
34395      getElementSize : function(s){
34396         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34397             return s.resizingEl.getWidth();
34398         }else{
34399             return s.resizingEl.getHeight();
34400         }
34401     },
34402     
34403     /**
34404      * Called after drag operations to set the size of the resizing element.
34405      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34406      * @param {Number} newSize The new size to set
34407      * @param {Function} onComplete A function to be invoked when resizing is complete
34408      */
34409     setElementSize : function(s, newSize, onComplete){
34410         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34411             if(!s.animate){
34412                 s.resizingEl.setWidth(newSize);
34413                 if(onComplete){
34414                     onComplete(s, newSize);
34415                 }
34416             }else{
34417                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34418             }
34419         }else{
34420             
34421             if(!s.animate){
34422                 s.resizingEl.setHeight(newSize);
34423                 if(onComplete){
34424                     onComplete(s, newSize);
34425                 }
34426             }else{
34427                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34428             }
34429         }
34430     }
34431 };
34432
34433 /** 
34434  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34435  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34436  * Adapter that  moves the splitter element to align with the resized sizing element. 
34437  * Used with an absolute positioned SplitBar.
34438  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34439  * document.body, make sure you assign an id to the body element.
34440  */
34441 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34442     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34443     this.container = Roo.get(container);
34444 };
34445
34446 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34447     init : function(s){
34448         this.basic.init(s);
34449     },
34450     
34451     getElementSize : function(s){
34452         return this.basic.getElementSize(s);
34453     },
34454     
34455     setElementSize : function(s, newSize, onComplete){
34456         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34457     },
34458     
34459     moveSplitter : function(s){
34460         var yes = Roo.bootstrap.SplitBar;
34461         switch(s.placement){
34462             case yes.LEFT:
34463                 s.el.setX(s.resizingEl.getRight());
34464                 break;
34465             case yes.RIGHT:
34466                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34467                 break;
34468             case yes.TOP:
34469                 s.el.setY(s.resizingEl.getBottom());
34470                 break;
34471             case yes.BOTTOM:
34472                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34473                 break;
34474         }
34475     }
34476 };
34477
34478 /**
34479  * Orientation constant - Create a vertical SplitBar
34480  * @static
34481  * @type Number
34482  */
34483 Roo.bootstrap.SplitBar.VERTICAL = 1;
34484
34485 /**
34486  * Orientation constant - Create a horizontal SplitBar
34487  * @static
34488  * @type Number
34489  */
34490 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34491
34492 /**
34493  * Placement constant - The resizing element is to the left of the splitter element
34494  * @static
34495  * @type Number
34496  */
34497 Roo.bootstrap.SplitBar.LEFT = 1;
34498
34499 /**
34500  * Placement constant - The resizing element is to the right of the splitter element
34501  * @static
34502  * @type Number
34503  */
34504 Roo.bootstrap.SplitBar.RIGHT = 2;
34505
34506 /**
34507  * Placement constant - The resizing element is positioned above the splitter element
34508  * @static
34509  * @type Number
34510  */
34511 Roo.bootstrap.SplitBar.TOP = 3;
34512
34513 /**
34514  * Placement constant - The resizing element is positioned under splitter element
34515  * @static
34516  * @type Number
34517  */
34518 Roo.bootstrap.SplitBar.BOTTOM = 4;
34519 Roo.namespace("Roo.bootstrap.layout");/*
34520  * Based on:
34521  * Ext JS Library 1.1.1
34522  * Copyright(c) 2006-2007, Ext JS, LLC.
34523  *
34524  * Originally Released Under LGPL - original licence link has changed is not relivant.
34525  *
34526  * Fork - LGPL
34527  * <script type="text/javascript">
34528  */
34529
34530 /**
34531  * @class Roo.bootstrap.layout.Manager
34532  * @extends Roo.bootstrap.Component
34533  * Base class for layout managers.
34534  */
34535 Roo.bootstrap.layout.Manager = function(config)
34536 {
34537     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34538
34539
34540
34541
34542
34543     /** false to disable window resize monitoring @type Boolean */
34544     this.monitorWindowResize = true;
34545     this.regions = {};
34546     this.addEvents({
34547         /**
34548          * @event layout
34549          * Fires when a layout is performed.
34550          * @param {Roo.LayoutManager} this
34551          */
34552         "layout" : true,
34553         /**
34554          * @event regionresized
34555          * Fires when the user resizes a region.
34556          * @param {Roo.LayoutRegion} region The resized region
34557          * @param {Number} newSize The new size (width for east/west, height for north/south)
34558          */
34559         "regionresized" : true,
34560         /**
34561          * @event regioncollapsed
34562          * Fires when a region is collapsed.
34563          * @param {Roo.LayoutRegion} region The collapsed region
34564          */
34565         "regioncollapsed" : true,
34566         /**
34567          * @event regionexpanded
34568          * Fires when a region is expanded.
34569          * @param {Roo.LayoutRegion} region The expanded region
34570          */
34571         "regionexpanded" : true
34572     });
34573     this.updating = false;
34574
34575     if (config.el) {
34576         this.el = Roo.get(config.el);
34577         this.initEvents();
34578     }
34579
34580 };
34581
34582 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34583
34584
34585     regions : null,
34586
34587     monitorWindowResize : true,
34588
34589
34590     updating : false,
34591
34592
34593     onRender : function(ct, position)
34594     {
34595         if(!this.el){
34596             this.el = Roo.get(ct);
34597             this.initEvents();
34598         }
34599         //this.fireEvent('render',this);
34600     },
34601
34602
34603     initEvents: function()
34604     {
34605
34606
34607         // ie scrollbar fix
34608         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34609             document.body.scroll = "no";
34610         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34611             this.el.position('relative');
34612         }
34613         this.id = this.el.id;
34614         this.el.addClass("roo-layout-container");
34615         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34616         if(this.el.dom != document.body ) {
34617             this.el.on('resize', this.layout,this);
34618             this.el.on('show', this.layout,this);
34619         }
34620
34621     },
34622
34623     /**
34624      * Returns true if this layout is currently being updated
34625      * @return {Boolean}
34626      */
34627     isUpdating : function(){
34628         return this.updating;
34629     },
34630
34631     /**
34632      * Suspend the LayoutManager from doing auto-layouts while
34633      * making multiple add or remove calls
34634      */
34635     beginUpdate : function(){
34636         this.updating = true;
34637     },
34638
34639     /**
34640      * Restore auto-layouts and optionally disable the manager from performing a layout
34641      * @param {Boolean} noLayout true to disable a layout update
34642      */
34643     endUpdate : function(noLayout){
34644         this.updating = false;
34645         if(!noLayout){
34646             this.layout();
34647         }
34648     },
34649
34650     layout: function(){
34651         // abstract...
34652     },
34653
34654     onRegionResized : function(region, newSize){
34655         this.fireEvent("regionresized", region, newSize);
34656         this.layout();
34657     },
34658
34659     onRegionCollapsed : function(region){
34660         this.fireEvent("regioncollapsed", region);
34661     },
34662
34663     onRegionExpanded : function(region){
34664         this.fireEvent("regionexpanded", region);
34665     },
34666
34667     /**
34668      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34669      * performs box-model adjustments.
34670      * @return {Object} The size as an object {width: (the width), height: (the height)}
34671      */
34672     getViewSize : function()
34673     {
34674         var size;
34675         if(this.el.dom != document.body){
34676             size = this.el.getSize();
34677         }else{
34678             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34679         }
34680         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34681         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34682         return size;
34683     },
34684
34685     /**
34686      * Returns the Element this layout is bound to.
34687      * @return {Roo.Element}
34688      */
34689     getEl : function(){
34690         return this.el;
34691     },
34692
34693     /**
34694      * Returns the specified region.
34695      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34696      * @return {Roo.LayoutRegion}
34697      */
34698     getRegion : function(target){
34699         return this.regions[target.toLowerCase()];
34700     },
34701
34702     onWindowResize : function(){
34703         if(this.monitorWindowResize){
34704             this.layout();
34705         }
34706     }
34707 });
34708 /*
34709  * Based on:
34710  * Ext JS Library 1.1.1
34711  * Copyright(c) 2006-2007, Ext JS, LLC.
34712  *
34713  * Originally Released Under LGPL - original licence link has changed is not relivant.
34714  *
34715  * Fork - LGPL
34716  * <script type="text/javascript">
34717  */
34718 /**
34719  * @class Roo.bootstrap.layout.Border
34720  * @extends Roo.bootstrap.layout.Manager
34721  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34722  * please see: examples/bootstrap/nested.html<br><br>
34723  
34724 <b>The container the layout is rendered into can be either the body element or any other element.
34725 If it is not the body element, the container needs to either be an absolute positioned element,
34726 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34727 the container size if it is not the body element.</b>
34728
34729 * @constructor
34730 * Create a new Border
34731 * @param {Object} config Configuration options
34732  */
34733 Roo.bootstrap.layout.Border = function(config){
34734     config = config || {};
34735     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34736     
34737     
34738     
34739     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34740         if(config[region]){
34741             config[region].region = region;
34742             this.addRegion(config[region]);
34743         }
34744     },this);
34745     
34746 };
34747
34748 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34749
34750 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34751     /**
34752      * Creates and adds a new region if it doesn't already exist.
34753      * @param {String} target The target region key (north, south, east, west or center).
34754      * @param {Object} config The regions config object
34755      * @return {BorderLayoutRegion} The new region
34756      */
34757     addRegion : function(config)
34758     {
34759         if(!this.regions[config.region]){
34760             var r = this.factory(config);
34761             this.bindRegion(r);
34762         }
34763         return this.regions[config.region];
34764     },
34765
34766     // private (kinda)
34767     bindRegion : function(r){
34768         this.regions[r.config.region] = r;
34769         
34770         r.on("visibilitychange",    this.layout, this);
34771         r.on("paneladded",          this.layout, this);
34772         r.on("panelremoved",        this.layout, this);
34773         r.on("invalidated",         this.layout, this);
34774         r.on("resized",             this.onRegionResized, this);
34775         r.on("collapsed",           this.onRegionCollapsed, this);
34776         r.on("expanded",            this.onRegionExpanded, this);
34777     },
34778
34779     /**
34780      * Performs a layout update.
34781      */
34782     layout : function()
34783     {
34784         if(this.updating) {
34785             return;
34786         }
34787         
34788         // render all the rebions if they have not been done alreayd?
34789         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34790             if(this.regions[region] && !this.regions[region].bodyEl){
34791                 this.regions[region].onRender(this.el)
34792             }
34793         },this);
34794         
34795         var size = this.getViewSize();
34796         var w = size.width;
34797         var h = size.height;
34798         var centerW = w;
34799         var centerH = h;
34800         var centerY = 0;
34801         var centerX = 0;
34802         //var x = 0, y = 0;
34803
34804         var rs = this.regions;
34805         var north = rs["north"];
34806         var south = rs["south"]; 
34807         var west = rs["west"];
34808         var east = rs["east"];
34809         var center = rs["center"];
34810         //if(this.hideOnLayout){ // not supported anymore
34811             //c.el.setStyle("display", "none");
34812         //}
34813         if(north && north.isVisible()){
34814             var b = north.getBox();
34815             var m = north.getMargins();
34816             b.width = w - (m.left+m.right);
34817             b.x = m.left;
34818             b.y = m.top;
34819             centerY = b.height + b.y + m.bottom;
34820             centerH -= centerY;
34821             north.updateBox(this.safeBox(b));
34822         }
34823         if(south && south.isVisible()){
34824             var b = south.getBox();
34825             var m = south.getMargins();
34826             b.width = w - (m.left+m.right);
34827             b.x = m.left;
34828             var totalHeight = (b.height + m.top + m.bottom);
34829             b.y = h - totalHeight + m.top;
34830             centerH -= totalHeight;
34831             south.updateBox(this.safeBox(b));
34832         }
34833         if(west && west.isVisible()){
34834             var b = west.getBox();
34835             var m = west.getMargins();
34836             b.height = centerH - (m.top+m.bottom);
34837             b.x = m.left;
34838             b.y = centerY + m.top;
34839             var totalWidth = (b.width + m.left + m.right);
34840             centerX += totalWidth;
34841             centerW -= totalWidth;
34842             west.updateBox(this.safeBox(b));
34843         }
34844         if(east && east.isVisible()){
34845             var b = east.getBox();
34846             var m = east.getMargins();
34847             b.height = centerH - (m.top+m.bottom);
34848             var totalWidth = (b.width + m.left + m.right);
34849             b.x = w - totalWidth + m.left;
34850             b.y = centerY + m.top;
34851             centerW -= totalWidth;
34852             east.updateBox(this.safeBox(b));
34853         }
34854         if(center){
34855             var m = center.getMargins();
34856             var centerBox = {
34857                 x: centerX + m.left,
34858                 y: centerY + m.top,
34859                 width: centerW - (m.left+m.right),
34860                 height: centerH - (m.top+m.bottom)
34861             };
34862             //if(this.hideOnLayout){
34863                 //center.el.setStyle("display", "block");
34864             //}
34865             center.updateBox(this.safeBox(centerBox));
34866         }
34867         this.el.repaint();
34868         this.fireEvent("layout", this);
34869     },
34870
34871     // private
34872     safeBox : function(box){
34873         box.width = Math.max(0, box.width);
34874         box.height = Math.max(0, box.height);
34875         return box;
34876     },
34877
34878     /**
34879      * Adds a ContentPanel (or subclass) to this layout.
34880      * @param {String} target The target region key (north, south, east, west or center).
34881      * @param {Roo.ContentPanel} panel The panel to add
34882      * @return {Roo.ContentPanel} The added panel
34883      */
34884     add : function(target, panel){
34885          
34886         target = target.toLowerCase();
34887         return this.regions[target].add(panel);
34888     },
34889
34890     /**
34891      * Remove a ContentPanel (or subclass) to this layout.
34892      * @param {String} target The target region key (north, south, east, west or center).
34893      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34894      * @return {Roo.ContentPanel} The removed panel
34895      */
34896     remove : function(target, panel){
34897         target = target.toLowerCase();
34898         return this.regions[target].remove(panel);
34899     },
34900
34901     /**
34902      * Searches all regions for a panel with the specified id
34903      * @param {String} panelId
34904      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34905      */
34906     findPanel : function(panelId){
34907         var rs = this.regions;
34908         for(var target in rs){
34909             if(typeof rs[target] != "function"){
34910                 var p = rs[target].getPanel(panelId);
34911                 if(p){
34912                     return p;
34913                 }
34914             }
34915         }
34916         return null;
34917     },
34918
34919     /**
34920      * Searches all regions for a panel with the specified id and activates (shows) it.
34921      * @param {String/ContentPanel} panelId The panels id or the panel itself
34922      * @return {Roo.ContentPanel} The shown panel or null
34923      */
34924     showPanel : function(panelId) {
34925       var rs = this.regions;
34926       for(var target in rs){
34927          var r = rs[target];
34928          if(typeof r != "function"){
34929             if(r.hasPanel(panelId)){
34930                return r.showPanel(panelId);
34931             }
34932          }
34933       }
34934       return null;
34935    },
34936
34937    /**
34938      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34939      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34940      */
34941    /*
34942     restoreState : function(provider){
34943         if(!provider){
34944             provider = Roo.state.Manager;
34945         }
34946         var sm = new Roo.LayoutStateManager();
34947         sm.init(this, provider);
34948     },
34949 */
34950  
34951  
34952     /**
34953      * Adds a xtype elements to the layout.
34954      * <pre><code>
34955
34956 layout.addxtype({
34957        xtype : 'ContentPanel',
34958        region: 'west',
34959        items: [ .... ]
34960    }
34961 );
34962
34963 layout.addxtype({
34964         xtype : 'NestedLayoutPanel',
34965         region: 'west',
34966         layout: {
34967            center: { },
34968            west: { }   
34969         },
34970         items : [ ... list of content panels or nested layout panels.. ]
34971    }
34972 );
34973 </code></pre>
34974      * @param {Object} cfg Xtype definition of item to add.
34975      */
34976     addxtype : function(cfg)
34977     {
34978         // basically accepts a pannel...
34979         // can accept a layout region..!?!?
34980         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34981         
34982         
34983         // theory?  children can only be panels??
34984         
34985         //if (!cfg.xtype.match(/Panel$/)) {
34986         //    return false;
34987         //}
34988         var ret = false;
34989         
34990         if (typeof(cfg.region) == 'undefined') {
34991             Roo.log("Failed to add Panel, region was not set");
34992             Roo.log(cfg);
34993             return false;
34994         }
34995         var region = cfg.region;
34996         delete cfg.region;
34997         
34998           
34999         var xitems = [];
35000         if (cfg.items) {
35001             xitems = cfg.items;
35002             delete cfg.items;
35003         }
35004         var nb = false;
35005         
35006         switch(cfg.xtype) 
35007         {
35008             case 'Content':  // ContentPanel (el, cfg)
35009             case 'Scroll':  // ContentPanel (el, cfg)
35010             case 'View': 
35011                 cfg.autoCreate = true;
35012                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35013                 //} else {
35014                 //    var el = this.el.createChild();
35015                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35016                 //}
35017                 
35018                 this.add(region, ret);
35019                 break;
35020             
35021             /*
35022             case 'TreePanel': // our new panel!
35023                 cfg.el = this.el.createChild();
35024                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35025                 this.add(region, ret);
35026                 break;
35027             */
35028             
35029             case 'Nest': 
35030                 // create a new Layout (which is  a Border Layout...
35031                 
35032                 var clayout = cfg.layout;
35033                 clayout.el  = this.el.createChild();
35034                 clayout.items   = clayout.items  || [];
35035                 
35036                 delete cfg.layout;
35037                 
35038                 // replace this exitems with the clayout ones..
35039                 xitems = clayout.items;
35040                  
35041                 // force background off if it's in center...
35042                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35043                     cfg.background = false;
35044                 }
35045                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35046                 
35047                 
35048                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35049                 //console.log('adding nested layout panel '  + cfg.toSource());
35050                 this.add(region, ret);
35051                 nb = {}; /// find first...
35052                 break;
35053             
35054             case 'Grid':
35055                 
35056                 // needs grid and region
35057                 
35058                 //var el = this.getRegion(region).el.createChild();
35059                 /*
35060                  *var el = this.el.createChild();
35061                 // create the grid first...
35062                 cfg.grid.container = el;
35063                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35064                 */
35065                 
35066                 if (region == 'center' && this.active ) {
35067                     cfg.background = false;
35068                 }
35069                 
35070                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35071                 
35072                 this.add(region, ret);
35073                 /*
35074                 if (cfg.background) {
35075                     // render grid on panel activation (if panel background)
35076                     ret.on('activate', function(gp) {
35077                         if (!gp.grid.rendered) {
35078                     //        gp.grid.render(el);
35079                         }
35080                     });
35081                 } else {
35082                   //  cfg.grid.render(el);
35083                 }
35084                 */
35085                 break;
35086            
35087            
35088             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35089                 // it was the old xcomponent building that caused this before.
35090                 // espeically if border is the top element in the tree.
35091                 ret = this;
35092                 break; 
35093                 
35094                     
35095                 
35096                 
35097                 
35098             default:
35099                 /*
35100                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35101                     
35102                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35103                     this.add(region, ret);
35104                 } else {
35105                 */
35106                     Roo.log(cfg);
35107                     throw "Can not add '" + cfg.xtype + "' to Border";
35108                     return null;
35109              
35110                                 
35111              
35112         }
35113         this.beginUpdate();
35114         // add children..
35115         var region = '';
35116         var abn = {};
35117         Roo.each(xitems, function(i)  {
35118             region = nb && i.region ? i.region : false;
35119             
35120             var add = ret.addxtype(i);
35121            
35122             if (region) {
35123                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35124                 if (!i.background) {
35125                     abn[region] = nb[region] ;
35126                 }
35127             }
35128             
35129         });
35130         this.endUpdate();
35131
35132         // make the last non-background panel active..
35133         //if (nb) { Roo.log(abn); }
35134         if (nb) {
35135             
35136             for(var r in abn) {
35137                 region = this.getRegion(r);
35138                 if (region) {
35139                     // tried using nb[r], but it does not work..
35140                      
35141                     region.showPanel(abn[r]);
35142                    
35143                 }
35144             }
35145         }
35146         return ret;
35147         
35148     },
35149     
35150     
35151 // private
35152     factory : function(cfg)
35153     {
35154         
35155         var validRegions = Roo.bootstrap.layout.Border.regions;
35156
35157         var target = cfg.region;
35158         cfg.mgr = this;
35159         
35160         var r = Roo.bootstrap.layout;
35161         Roo.log(target);
35162         switch(target){
35163             case "north":
35164                 return new r.North(cfg);
35165             case "south":
35166                 return new r.South(cfg);
35167             case "east":
35168                 return new r.East(cfg);
35169             case "west":
35170                 return new r.West(cfg);
35171             case "center":
35172                 return new r.Center(cfg);
35173         }
35174         throw 'Layout region "'+target+'" not supported.';
35175     }
35176     
35177     
35178 });
35179  /*
35180  * Based on:
35181  * Ext JS Library 1.1.1
35182  * Copyright(c) 2006-2007, Ext JS, LLC.
35183  *
35184  * Originally Released Under LGPL - original licence link has changed is not relivant.
35185  *
35186  * Fork - LGPL
35187  * <script type="text/javascript">
35188  */
35189  
35190 /**
35191  * @class Roo.bootstrap.layout.Basic
35192  * @extends Roo.util.Observable
35193  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35194  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35195  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35196  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35197  * @cfg {string}   region  the region that it inhabits..
35198  * @cfg {bool}   skipConfig skip config?
35199  * 
35200
35201  */
35202 Roo.bootstrap.layout.Basic = function(config){
35203     
35204     this.mgr = config.mgr;
35205     
35206     this.position = config.region;
35207     
35208     var skipConfig = config.skipConfig;
35209     
35210     this.events = {
35211         /**
35212          * @scope Roo.BasicLayoutRegion
35213          */
35214         
35215         /**
35216          * @event beforeremove
35217          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35218          * @param {Roo.LayoutRegion} this
35219          * @param {Roo.ContentPanel} panel The panel
35220          * @param {Object} e The cancel event object
35221          */
35222         "beforeremove" : true,
35223         /**
35224          * @event invalidated
35225          * Fires when the layout for this region is changed.
35226          * @param {Roo.LayoutRegion} this
35227          */
35228         "invalidated" : true,
35229         /**
35230          * @event visibilitychange
35231          * Fires when this region is shown or hidden 
35232          * @param {Roo.LayoutRegion} this
35233          * @param {Boolean} visibility true or false
35234          */
35235         "visibilitychange" : true,
35236         /**
35237          * @event paneladded
35238          * Fires when a panel is added. 
35239          * @param {Roo.LayoutRegion} this
35240          * @param {Roo.ContentPanel} panel The panel
35241          */
35242         "paneladded" : true,
35243         /**
35244          * @event panelremoved
35245          * Fires when a panel is removed. 
35246          * @param {Roo.LayoutRegion} this
35247          * @param {Roo.ContentPanel} panel The panel
35248          */
35249         "panelremoved" : true,
35250         /**
35251          * @event beforecollapse
35252          * Fires when this region before collapse.
35253          * @param {Roo.LayoutRegion} this
35254          */
35255         "beforecollapse" : true,
35256         /**
35257          * @event collapsed
35258          * Fires when this region is collapsed.
35259          * @param {Roo.LayoutRegion} this
35260          */
35261         "collapsed" : true,
35262         /**
35263          * @event expanded
35264          * Fires when this region is expanded.
35265          * @param {Roo.LayoutRegion} this
35266          */
35267         "expanded" : true,
35268         /**
35269          * @event slideshow
35270          * Fires when this region is slid into view.
35271          * @param {Roo.LayoutRegion} this
35272          */
35273         "slideshow" : true,
35274         /**
35275          * @event slidehide
35276          * Fires when this region slides out of view. 
35277          * @param {Roo.LayoutRegion} this
35278          */
35279         "slidehide" : true,
35280         /**
35281          * @event panelactivated
35282          * Fires when a panel is activated. 
35283          * @param {Roo.LayoutRegion} this
35284          * @param {Roo.ContentPanel} panel The activated panel
35285          */
35286         "panelactivated" : true,
35287         /**
35288          * @event resized
35289          * Fires when the user resizes this region. 
35290          * @param {Roo.LayoutRegion} this
35291          * @param {Number} newSize The new size (width for east/west, height for north/south)
35292          */
35293         "resized" : true
35294     };
35295     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35296     this.panels = new Roo.util.MixedCollection();
35297     this.panels.getKey = this.getPanelId.createDelegate(this);
35298     this.box = null;
35299     this.activePanel = null;
35300     // ensure listeners are added...
35301     
35302     if (config.listeners || config.events) {
35303         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35304             listeners : config.listeners || {},
35305             events : config.events || {}
35306         });
35307     }
35308     
35309     if(skipConfig !== true){
35310         this.applyConfig(config);
35311     }
35312 };
35313
35314 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35315 {
35316     getPanelId : function(p){
35317         return p.getId();
35318     },
35319     
35320     applyConfig : function(config){
35321         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35322         this.config = config;
35323         
35324     },
35325     
35326     /**
35327      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35328      * the width, for horizontal (north, south) the height.
35329      * @param {Number} newSize The new width or height
35330      */
35331     resizeTo : function(newSize){
35332         var el = this.el ? this.el :
35333                  (this.activePanel ? this.activePanel.getEl() : null);
35334         if(el){
35335             switch(this.position){
35336                 case "east":
35337                 case "west":
35338                     el.setWidth(newSize);
35339                     this.fireEvent("resized", this, newSize);
35340                 break;
35341                 case "north":
35342                 case "south":
35343                     el.setHeight(newSize);
35344                     this.fireEvent("resized", this, newSize);
35345                 break;                
35346             }
35347         }
35348     },
35349     
35350     getBox : function(){
35351         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35352     },
35353     
35354     getMargins : function(){
35355         return this.margins;
35356     },
35357     
35358     updateBox : function(box){
35359         this.box = box;
35360         var el = this.activePanel.getEl();
35361         el.dom.style.left = box.x + "px";
35362         el.dom.style.top = box.y + "px";
35363         this.activePanel.setSize(box.width, box.height);
35364     },
35365     
35366     /**
35367      * Returns the container element for this region.
35368      * @return {Roo.Element}
35369      */
35370     getEl : function(){
35371         return this.activePanel;
35372     },
35373     
35374     /**
35375      * Returns true if this region is currently visible.
35376      * @return {Boolean}
35377      */
35378     isVisible : function(){
35379         return this.activePanel ? true : false;
35380     },
35381     
35382     setActivePanel : function(panel){
35383         panel = this.getPanel(panel);
35384         if(this.activePanel && this.activePanel != panel){
35385             this.activePanel.setActiveState(false);
35386             this.activePanel.getEl().setLeftTop(-10000,-10000);
35387         }
35388         this.activePanel = panel;
35389         panel.setActiveState(true);
35390         if(this.box){
35391             panel.setSize(this.box.width, this.box.height);
35392         }
35393         this.fireEvent("panelactivated", this, panel);
35394         this.fireEvent("invalidated");
35395     },
35396     
35397     /**
35398      * Show the specified panel.
35399      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35400      * @return {Roo.ContentPanel} The shown panel or null
35401      */
35402     showPanel : function(panel){
35403         panel = this.getPanel(panel);
35404         if(panel){
35405             this.setActivePanel(panel);
35406         }
35407         return panel;
35408     },
35409     
35410     /**
35411      * Get the active panel for this region.
35412      * @return {Roo.ContentPanel} The active panel or null
35413      */
35414     getActivePanel : function(){
35415         return this.activePanel;
35416     },
35417     
35418     /**
35419      * Add the passed ContentPanel(s)
35420      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35421      * @return {Roo.ContentPanel} The panel added (if only one was added)
35422      */
35423     add : function(panel){
35424         if(arguments.length > 1){
35425             for(var i = 0, len = arguments.length; i < len; i++) {
35426                 this.add(arguments[i]);
35427             }
35428             return null;
35429         }
35430         if(this.hasPanel(panel)){
35431             this.showPanel(panel);
35432             return panel;
35433         }
35434         var el = panel.getEl();
35435         if(el.dom.parentNode != this.mgr.el.dom){
35436             this.mgr.el.dom.appendChild(el.dom);
35437         }
35438         if(panel.setRegion){
35439             panel.setRegion(this);
35440         }
35441         this.panels.add(panel);
35442         el.setStyle("position", "absolute");
35443         if(!panel.background){
35444             this.setActivePanel(panel);
35445             if(this.config.initialSize && this.panels.getCount()==1){
35446                 this.resizeTo(this.config.initialSize);
35447             }
35448         }
35449         this.fireEvent("paneladded", this, panel);
35450         return panel;
35451     },
35452     
35453     /**
35454      * Returns true if the panel is in this region.
35455      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35456      * @return {Boolean}
35457      */
35458     hasPanel : function(panel){
35459         if(typeof panel == "object"){ // must be panel obj
35460             panel = panel.getId();
35461         }
35462         return this.getPanel(panel) ? true : false;
35463     },
35464     
35465     /**
35466      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35467      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35468      * @param {Boolean} preservePanel Overrides the config preservePanel option
35469      * @return {Roo.ContentPanel} The panel that was removed
35470      */
35471     remove : function(panel, preservePanel){
35472         panel = this.getPanel(panel);
35473         if(!panel){
35474             return null;
35475         }
35476         var e = {};
35477         this.fireEvent("beforeremove", this, panel, e);
35478         if(e.cancel === true){
35479             return null;
35480         }
35481         var panelId = panel.getId();
35482         this.panels.removeKey(panelId);
35483         return panel;
35484     },
35485     
35486     /**
35487      * Returns the panel specified or null if it's not in this region.
35488      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35489      * @return {Roo.ContentPanel}
35490      */
35491     getPanel : function(id){
35492         if(typeof id == "object"){ // must be panel obj
35493             return id;
35494         }
35495         return this.panels.get(id);
35496     },
35497     
35498     /**
35499      * Returns this regions position (north/south/east/west/center).
35500      * @return {String} 
35501      */
35502     getPosition: function(){
35503         return this.position;    
35504     }
35505 });/*
35506  * Based on:
35507  * Ext JS Library 1.1.1
35508  * Copyright(c) 2006-2007, Ext JS, LLC.
35509  *
35510  * Originally Released Under LGPL - original licence link has changed is not relivant.
35511  *
35512  * Fork - LGPL
35513  * <script type="text/javascript">
35514  */
35515  
35516 /**
35517  * @class Roo.bootstrap.layout.Region
35518  * @extends Roo.bootstrap.layout.Basic
35519  * This class represents a region in a layout manager.
35520  
35521  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35522  * @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})
35523  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35524  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35525  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35526  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35527  * @cfg {String}    title           The title for the region (overrides panel titles)
35528  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35529  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35530  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35531  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35532  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35533  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35534  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35535  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35536  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35537  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35538
35539  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35540  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35541  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35542  * @cfg {Number}    width           For East/West panels
35543  * @cfg {Number}    height          For North/South panels
35544  * @cfg {Boolean}   split           To show the splitter
35545  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35546  * 
35547  * @cfg {string}   cls             Extra CSS classes to add to region
35548  * 
35549  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35550  * @cfg {string}   region  the region that it inhabits..
35551  *
35552
35553  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35554  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35555
35556  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35557  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35558  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35559  */
35560 Roo.bootstrap.layout.Region = function(config)
35561 {
35562     this.applyConfig(config);
35563
35564     var mgr = config.mgr;
35565     var pos = config.region;
35566     config.skipConfig = true;
35567     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35568     
35569     if (mgr.el) {
35570         this.onRender(mgr.el);   
35571     }
35572      
35573     this.visible = true;
35574     this.collapsed = false;
35575     this.unrendered_panels = [];
35576 };
35577
35578 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35579
35580     position: '', // set by wrapper (eg. north/south etc..)
35581     unrendered_panels : null,  // unrendered panels.
35582     createBody : function(){
35583         /** This region's body element 
35584         * @type Roo.Element */
35585         this.bodyEl = this.el.createChild({
35586                 tag: "div",
35587                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35588         });
35589     },
35590
35591     onRender: function(ctr, pos)
35592     {
35593         var dh = Roo.DomHelper;
35594         /** This region's container element 
35595         * @type Roo.Element */
35596         this.el = dh.append(ctr.dom, {
35597                 tag: "div",
35598                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35599             }, true);
35600         /** This region's title element 
35601         * @type Roo.Element */
35602     
35603         this.titleEl = dh.append(this.el.dom,
35604             {
35605                     tag: "div",
35606                     unselectable: "on",
35607                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35608                     children:[
35609                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35610                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35611                     ]}, true);
35612         
35613         this.titleEl.enableDisplayMode();
35614         /** This region's title text element 
35615         * @type HTMLElement */
35616         this.titleTextEl = this.titleEl.dom.firstChild;
35617         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35618         /*
35619         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35620         this.closeBtn.enableDisplayMode();
35621         this.closeBtn.on("click", this.closeClicked, this);
35622         this.closeBtn.hide();
35623     */
35624         this.createBody(this.config);
35625         if(this.config.hideWhenEmpty){
35626             this.hide();
35627             this.on("paneladded", this.validateVisibility, this);
35628             this.on("panelremoved", this.validateVisibility, this);
35629         }
35630         if(this.autoScroll){
35631             this.bodyEl.setStyle("overflow", "auto");
35632         }else{
35633             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35634         }
35635         //if(c.titlebar !== false){
35636             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35637                 this.titleEl.hide();
35638             }else{
35639                 this.titleEl.show();
35640                 if(this.config.title){
35641                     this.titleTextEl.innerHTML = this.config.title;
35642                 }
35643             }
35644         //}
35645         if(this.config.collapsed){
35646             this.collapse(true);
35647         }
35648         if(this.config.hidden){
35649             this.hide();
35650         }
35651         
35652         if (this.unrendered_panels && this.unrendered_panels.length) {
35653             for (var i =0;i< this.unrendered_panels.length; i++) {
35654                 this.add(this.unrendered_panels[i]);
35655             }
35656             this.unrendered_panels = null;
35657             
35658         }
35659         
35660     },
35661     
35662     applyConfig : function(c)
35663     {
35664         /*
35665          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35666             var dh = Roo.DomHelper;
35667             if(c.titlebar !== false){
35668                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35669                 this.collapseBtn.on("click", this.collapse, this);
35670                 this.collapseBtn.enableDisplayMode();
35671                 /*
35672                 if(c.showPin === true || this.showPin){
35673                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35674                     this.stickBtn.enableDisplayMode();
35675                     this.stickBtn.on("click", this.expand, this);
35676                     this.stickBtn.hide();
35677                 }
35678                 
35679             }
35680             */
35681             /** This region's collapsed element
35682             * @type Roo.Element */
35683             /*
35684              *
35685             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35686                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35687             ]}, true);
35688             
35689             if(c.floatable !== false){
35690                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35691                this.collapsedEl.on("click", this.collapseClick, this);
35692             }
35693
35694             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35695                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35696                    id: "message", unselectable: "on", style:{"float":"left"}});
35697                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35698              }
35699             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35700             this.expandBtn.on("click", this.expand, this);
35701             
35702         }
35703         
35704         if(this.collapseBtn){
35705             this.collapseBtn.setVisible(c.collapsible == true);
35706         }
35707         
35708         this.cmargins = c.cmargins || this.cmargins ||
35709                          (this.position == "west" || this.position == "east" ?
35710                              {top: 0, left: 2, right:2, bottom: 0} :
35711                              {top: 2, left: 0, right:0, bottom: 2});
35712         */
35713         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35714         
35715         
35716         this.bottomTabs = c.tabPosition != "top";
35717         
35718         this.autoScroll = c.autoScroll || false;
35719         
35720         
35721        
35722         
35723         this.duration = c.duration || .30;
35724         this.slideDuration = c.slideDuration || .45;
35725         this.config = c;
35726        
35727     },
35728     /**
35729      * Returns true if this region is currently visible.
35730      * @return {Boolean}
35731      */
35732     isVisible : function(){
35733         return this.visible;
35734     },
35735
35736     /**
35737      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35738      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35739      */
35740     //setCollapsedTitle : function(title){
35741     //    title = title || "&#160;";
35742      //   if(this.collapsedTitleTextEl){
35743       //      this.collapsedTitleTextEl.innerHTML = title;
35744        // }
35745     //},
35746
35747     getBox : function(){
35748         var b;
35749       //  if(!this.collapsed){
35750             b = this.el.getBox(false, true);
35751        // }else{
35752           //  b = this.collapsedEl.getBox(false, true);
35753         //}
35754         return b;
35755     },
35756
35757     getMargins : function(){
35758         return this.margins;
35759         //return this.collapsed ? this.cmargins : this.margins;
35760     },
35761 /*
35762     highlight : function(){
35763         this.el.addClass("x-layout-panel-dragover");
35764     },
35765
35766     unhighlight : function(){
35767         this.el.removeClass("x-layout-panel-dragover");
35768     },
35769 */
35770     updateBox : function(box)
35771     {
35772         if (!this.bodyEl) {
35773             return; // not rendered yet..
35774         }
35775         
35776         this.box = box;
35777         if(!this.collapsed){
35778             this.el.dom.style.left = box.x + "px";
35779             this.el.dom.style.top = box.y + "px";
35780             this.updateBody(box.width, box.height);
35781         }else{
35782             this.collapsedEl.dom.style.left = box.x + "px";
35783             this.collapsedEl.dom.style.top = box.y + "px";
35784             this.collapsedEl.setSize(box.width, box.height);
35785         }
35786         if(this.tabs){
35787             this.tabs.autoSizeTabs();
35788         }
35789     },
35790
35791     updateBody : function(w, h)
35792     {
35793         if(w !== null){
35794             this.el.setWidth(w);
35795             w -= this.el.getBorderWidth("rl");
35796             if(this.config.adjustments){
35797                 w += this.config.adjustments[0];
35798             }
35799         }
35800         if(h !== null && h > 0){
35801             this.el.setHeight(h);
35802             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35803             h -= this.el.getBorderWidth("tb");
35804             if(this.config.adjustments){
35805                 h += this.config.adjustments[1];
35806             }
35807             this.bodyEl.setHeight(h);
35808             if(this.tabs){
35809                 h = this.tabs.syncHeight(h);
35810             }
35811         }
35812         if(this.panelSize){
35813             w = w !== null ? w : this.panelSize.width;
35814             h = h !== null ? h : this.panelSize.height;
35815         }
35816         if(this.activePanel){
35817             var el = this.activePanel.getEl();
35818             w = w !== null ? w : el.getWidth();
35819             h = h !== null ? h : el.getHeight();
35820             this.panelSize = {width: w, height: h};
35821             this.activePanel.setSize(w, h);
35822         }
35823         if(Roo.isIE && this.tabs){
35824             this.tabs.el.repaint();
35825         }
35826     },
35827
35828     /**
35829      * Returns the container element for this region.
35830      * @return {Roo.Element}
35831      */
35832     getEl : function(){
35833         return this.el;
35834     },
35835
35836     /**
35837      * Hides this region.
35838      */
35839     hide : function(){
35840         //if(!this.collapsed){
35841             this.el.dom.style.left = "-2000px";
35842             this.el.hide();
35843         //}else{
35844          //   this.collapsedEl.dom.style.left = "-2000px";
35845          //   this.collapsedEl.hide();
35846        // }
35847         this.visible = false;
35848         this.fireEvent("visibilitychange", this, false);
35849     },
35850
35851     /**
35852      * Shows this region if it was previously hidden.
35853      */
35854     show : function(){
35855         //if(!this.collapsed){
35856             this.el.show();
35857         //}else{
35858         //    this.collapsedEl.show();
35859        // }
35860         this.visible = true;
35861         this.fireEvent("visibilitychange", this, true);
35862     },
35863 /*
35864     closeClicked : function(){
35865         if(this.activePanel){
35866             this.remove(this.activePanel);
35867         }
35868     },
35869
35870     collapseClick : function(e){
35871         if(this.isSlid){
35872            e.stopPropagation();
35873            this.slideIn();
35874         }else{
35875            e.stopPropagation();
35876            this.slideOut();
35877         }
35878     },
35879 */
35880     /**
35881      * Collapses this region.
35882      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35883      */
35884     /*
35885     collapse : function(skipAnim, skipCheck = false){
35886         if(this.collapsed) {
35887             return;
35888         }
35889         
35890         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35891             
35892             this.collapsed = true;
35893             if(this.split){
35894                 this.split.el.hide();
35895             }
35896             if(this.config.animate && skipAnim !== true){
35897                 this.fireEvent("invalidated", this);
35898                 this.animateCollapse();
35899             }else{
35900                 this.el.setLocation(-20000,-20000);
35901                 this.el.hide();
35902                 this.collapsedEl.show();
35903                 this.fireEvent("collapsed", this);
35904                 this.fireEvent("invalidated", this);
35905             }
35906         }
35907         
35908     },
35909 */
35910     animateCollapse : function(){
35911         // overridden
35912     },
35913
35914     /**
35915      * Expands this region if it was previously collapsed.
35916      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35917      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35918      */
35919     /*
35920     expand : function(e, skipAnim){
35921         if(e) {
35922             e.stopPropagation();
35923         }
35924         if(!this.collapsed || this.el.hasActiveFx()) {
35925             return;
35926         }
35927         if(this.isSlid){
35928             this.afterSlideIn();
35929             skipAnim = true;
35930         }
35931         this.collapsed = false;
35932         if(this.config.animate && skipAnim !== true){
35933             this.animateExpand();
35934         }else{
35935             this.el.show();
35936             if(this.split){
35937                 this.split.el.show();
35938             }
35939             this.collapsedEl.setLocation(-2000,-2000);
35940             this.collapsedEl.hide();
35941             this.fireEvent("invalidated", this);
35942             this.fireEvent("expanded", this);
35943         }
35944     },
35945 */
35946     animateExpand : function(){
35947         // overridden
35948     },
35949
35950     initTabs : function()
35951     {
35952         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35953         
35954         var ts = new Roo.bootstrap.panel.Tabs({
35955                 el: this.bodyEl.dom,
35956                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35957                 disableTooltips: this.config.disableTabTips,
35958                 toolbar : this.config.toolbar
35959             });
35960         
35961         if(this.config.hideTabs){
35962             ts.stripWrap.setDisplayed(false);
35963         }
35964         this.tabs = ts;
35965         ts.resizeTabs = this.config.resizeTabs === true;
35966         ts.minTabWidth = this.config.minTabWidth || 40;
35967         ts.maxTabWidth = this.config.maxTabWidth || 250;
35968         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35969         ts.monitorResize = false;
35970         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35971         ts.bodyEl.addClass('roo-layout-tabs-body');
35972         this.panels.each(this.initPanelAsTab, this);
35973     },
35974
35975     initPanelAsTab : function(panel){
35976         var ti = this.tabs.addTab(
35977             panel.getEl().id,
35978             panel.getTitle(),
35979             null,
35980             this.config.closeOnTab && panel.isClosable(),
35981             panel.tpl
35982         );
35983         if(panel.tabTip !== undefined){
35984             ti.setTooltip(panel.tabTip);
35985         }
35986         ti.on("activate", function(){
35987               this.setActivePanel(panel);
35988         }, this);
35989         
35990         if(this.config.closeOnTab){
35991             ti.on("beforeclose", function(t, e){
35992                 e.cancel = true;
35993                 this.remove(panel);
35994             }, this);
35995         }
35996         
35997         panel.tabItem = ti;
35998         
35999         return ti;
36000     },
36001
36002     updatePanelTitle : function(panel, title)
36003     {
36004         if(this.activePanel == panel){
36005             this.updateTitle(title);
36006         }
36007         if(this.tabs){
36008             var ti = this.tabs.getTab(panel.getEl().id);
36009             ti.setText(title);
36010             if(panel.tabTip !== undefined){
36011                 ti.setTooltip(panel.tabTip);
36012             }
36013         }
36014     },
36015
36016     updateTitle : function(title){
36017         if(this.titleTextEl && !this.config.title){
36018             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36019         }
36020     },
36021
36022     setActivePanel : function(panel)
36023     {
36024         panel = this.getPanel(panel);
36025         if(this.activePanel && this.activePanel != panel){
36026             if(this.activePanel.setActiveState(false) === false){
36027                 return;
36028             }
36029         }
36030         this.activePanel = panel;
36031         panel.setActiveState(true);
36032         if(this.panelSize){
36033             panel.setSize(this.panelSize.width, this.panelSize.height);
36034         }
36035         if(this.closeBtn){
36036             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36037         }
36038         this.updateTitle(panel.getTitle());
36039         if(this.tabs){
36040             this.fireEvent("invalidated", this);
36041         }
36042         this.fireEvent("panelactivated", this, panel);
36043     },
36044
36045     /**
36046      * Shows the specified panel.
36047      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36048      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36049      */
36050     showPanel : function(panel)
36051     {
36052         panel = this.getPanel(panel);
36053         if(panel){
36054             if(this.tabs){
36055                 var tab = this.tabs.getTab(panel.getEl().id);
36056                 if(tab.isHidden()){
36057                     this.tabs.unhideTab(tab.id);
36058                 }
36059                 tab.activate();
36060             }else{
36061                 this.setActivePanel(panel);
36062             }
36063         }
36064         return panel;
36065     },
36066
36067     /**
36068      * Get the active panel for this region.
36069      * @return {Roo.ContentPanel} The active panel or null
36070      */
36071     getActivePanel : function(){
36072         return this.activePanel;
36073     },
36074
36075     validateVisibility : function(){
36076         if(this.panels.getCount() < 1){
36077             this.updateTitle("&#160;");
36078             this.closeBtn.hide();
36079             this.hide();
36080         }else{
36081             if(!this.isVisible()){
36082                 this.show();
36083             }
36084         }
36085     },
36086
36087     /**
36088      * Adds the passed ContentPanel(s) to this region.
36089      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36090      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36091      */
36092     add : function(panel)
36093     {
36094         if(arguments.length > 1){
36095             for(var i = 0, len = arguments.length; i < len; i++) {
36096                 this.add(arguments[i]);
36097             }
36098             return null;
36099         }
36100         
36101         // if we have not been rendered yet, then we can not really do much of this..
36102         if (!this.bodyEl) {
36103             this.unrendered_panels.push(panel);
36104             return panel;
36105         }
36106         
36107         
36108         
36109         
36110         if(this.hasPanel(panel)){
36111             this.showPanel(panel);
36112             return panel;
36113         }
36114         panel.setRegion(this);
36115         this.panels.add(panel);
36116        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36117             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36118             // and hide them... ???
36119             this.bodyEl.dom.appendChild(panel.getEl().dom);
36120             if(panel.background !== true){
36121                 this.setActivePanel(panel);
36122             }
36123             this.fireEvent("paneladded", this, panel);
36124             return panel;
36125         }
36126         */
36127         if(!this.tabs){
36128             this.initTabs();
36129         }else{
36130             this.initPanelAsTab(panel);
36131         }
36132         
36133         
36134         if(panel.background !== true){
36135             this.tabs.activate(panel.getEl().id);
36136         }
36137         this.fireEvent("paneladded", this, panel);
36138         return panel;
36139     },
36140
36141     /**
36142      * Hides the tab for the specified panel.
36143      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36144      */
36145     hidePanel : function(panel){
36146         if(this.tabs && (panel = this.getPanel(panel))){
36147             this.tabs.hideTab(panel.getEl().id);
36148         }
36149     },
36150
36151     /**
36152      * Unhides the tab for a previously hidden panel.
36153      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36154      */
36155     unhidePanel : function(panel){
36156         if(this.tabs && (panel = this.getPanel(panel))){
36157             this.tabs.unhideTab(panel.getEl().id);
36158         }
36159     },
36160
36161     clearPanels : function(){
36162         while(this.panels.getCount() > 0){
36163              this.remove(this.panels.first());
36164         }
36165     },
36166
36167     /**
36168      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36169      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36170      * @param {Boolean} preservePanel Overrides the config preservePanel option
36171      * @return {Roo.ContentPanel} The panel that was removed
36172      */
36173     remove : function(panel, preservePanel)
36174     {
36175         panel = this.getPanel(panel);
36176         if(!panel){
36177             return null;
36178         }
36179         var e = {};
36180         this.fireEvent("beforeremove", this, panel, e);
36181         if(e.cancel === true){
36182             return null;
36183         }
36184         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36185         var panelId = panel.getId();
36186         this.panels.removeKey(panelId);
36187         if(preservePanel){
36188             document.body.appendChild(panel.getEl().dom);
36189         }
36190         if(this.tabs){
36191             this.tabs.removeTab(panel.getEl().id);
36192         }else if (!preservePanel){
36193             this.bodyEl.dom.removeChild(panel.getEl().dom);
36194         }
36195         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36196             var p = this.panels.first();
36197             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36198             tempEl.appendChild(p.getEl().dom);
36199             this.bodyEl.update("");
36200             this.bodyEl.dom.appendChild(p.getEl().dom);
36201             tempEl = null;
36202             this.updateTitle(p.getTitle());
36203             this.tabs = null;
36204             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36205             this.setActivePanel(p);
36206         }
36207         panel.setRegion(null);
36208         if(this.activePanel == panel){
36209             this.activePanel = null;
36210         }
36211         if(this.config.autoDestroy !== false && preservePanel !== true){
36212             try{panel.destroy();}catch(e){}
36213         }
36214         this.fireEvent("panelremoved", this, panel);
36215         return panel;
36216     },
36217
36218     /**
36219      * Returns the TabPanel component used by this region
36220      * @return {Roo.TabPanel}
36221      */
36222     getTabs : function(){
36223         return this.tabs;
36224     },
36225
36226     createTool : function(parentEl, className){
36227         var btn = Roo.DomHelper.append(parentEl, {
36228             tag: "div",
36229             cls: "x-layout-tools-button",
36230             children: [ {
36231                 tag: "div",
36232                 cls: "roo-layout-tools-button-inner " + className,
36233                 html: "&#160;"
36234             }]
36235         }, true);
36236         btn.addClassOnOver("roo-layout-tools-button-over");
36237         return btn;
36238     }
36239 });/*
36240  * Based on:
36241  * Ext JS Library 1.1.1
36242  * Copyright(c) 2006-2007, Ext JS, LLC.
36243  *
36244  * Originally Released Under LGPL - original licence link has changed is not relivant.
36245  *
36246  * Fork - LGPL
36247  * <script type="text/javascript">
36248  */
36249  
36250
36251
36252 /**
36253  * @class Roo.SplitLayoutRegion
36254  * @extends Roo.LayoutRegion
36255  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36256  */
36257 Roo.bootstrap.layout.Split = function(config){
36258     this.cursor = config.cursor;
36259     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36260 };
36261
36262 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36263 {
36264     splitTip : "Drag to resize.",
36265     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36266     useSplitTips : false,
36267
36268     applyConfig : function(config){
36269         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36270     },
36271     
36272     onRender : function(ctr,pos) {
36273         
36274         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36275         if(!this.config.split){
36276             return;
36277         }
36278         if(!this.split){
36279             
36280             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36281                             tag: "div",
36282                             id: this.el.id + "-split",
36283                             cls: "roo-layout-split roo-layout-split-"+this.position,
36284                             html: "&#160;"
36285             });
36286             /** The SplitBar for this region 
36287             * @type Roo.SplitBar */
36288             // does not exist yet...
36289             Roo.log([this.position, this.orientation]);
36290             
36291             this.split = new Roo.bootstrap.SplitBar({
36292                 dragElement : splitEl,
36293                 resizingElement: this.el,
36294                 orientation : this.orientation
36295             });
36296             
36297             this.split.on("moved", this.onSplitMove, this);
36298             this.split.useShim = this.config.useShim === true;
36299             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36300             if(this.useSplitTips){
36301                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36302             }
36303             //if(config.collapsible){
36304             //    this.split.el.on("dblclick", this.collapse,  this);
36305             //}
36306         }
36307         if(typeof this.config.minSize != "undefined"){
36308             this.split.minSize = this.config.minSize;
36309         }
36310         if(typeof this.config.maxSize != "undefined"){
36311             this.split.maxSize = this.config.maxSize;
36312         }
36313         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36314             this.hideSplitter();
36315         }
36316         
36317     },
36318
36319     getHMaxSize : function(){
36320          var cmax = this.config.maxSize || 10000;
36321          var center = this.mgr.getRegion("center");
36322          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36323     },
36324
36325     getVMaxSize : function(){
36326          var cmax = this.config.maxSize || 10000;
36327          var center = this.mgr.getRegion("center");
36328          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36329     },
36330
36331     onSplitMove : function(split, newSize){
36332         this.fireEvent("resized", this, newSize);
36333     },
36334     
36335     /** 
36336      * Returns the {@link Roo.SplitBar} for this region.
36337      * @return {Roo.SplitBar}
36338      */
36339     getSplitBar : function(){
36340         return this.split;
36341     },
36342     
36343     hide : function(){
36344         this.hideSplitter();
36345         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36346     },
36347
36348     hideSplitter : function(){
36349         if(this.split){
36350             this.split.el.setLocation(-2000,-2000);
36351             this.split.el.hide();
36352         }
36353     },
36354
36355     show : function(){
36356         if(this.split){
36357             this.split.el.show();
36358         }
36359         Roo.bootstrap.layout.Split.superclass.show.call(this);
36360     },
36361     
36362     beforeSlide: function(){
36363         if(Roo.isGecko){// firefox overflow auto bug workaround
36364             this.bodyEl.clip();
36365             if(this.tabs) {
36366                 this.tabs.bodyEl.clip();
36367             }
36368             if(this.activePanel){
36369                 this.activePanel.getEl().clip();
36370                 
36371                 if(this.activePanel.beforeSlide){
36372                     this.activePanel.beforeSlide();
36373                 }
36374             }
36375         }
36376     },
36377     
36378     afterSlide : function(){
36379         if(Roo.isGecko){// firefox overflow auto bug workaround
36380             this.bodyEl.unclip();
36381             if(this.tabs) {
36382                 this.tabs.bodyEl.unclip();
36383             }
36384             if(this.activePanel){
36385                 this.activePanel.getEl().unclip();
36386                 if(this.activePanel.afterSlide){
36387                     this.activePanel.afterSlide();
36388                 }
36389             }
36390         }
36391     },
36392
36393     initAutoHide : function(){
36394         if(this.autoHide !== false){
36395             if(!this.autoHideHd){
36396                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36397                 this.autoHideHd = {
36398                     "mouseout": function(e){
36399                         if(!e.within(this.el, true)){
36400                             st.delay(500);
36401                         }
36402                     },
36403                     "mouseover" : function(e){
36404                         st.cancel();
36405                     },
36406                     scope : this
36407                 };
36408             }
36409             this.el.on(this.autoHideHd);
36410         }
36411     },
36412
36413     clearAutoHide : function(){
36414         if(this.autoHide !== false){
36415             this.el.un("mouseout", this.autoHideHd.mouseout);
36416             this.el.un("mouseover", this.autoHideHd.mouseover);
36417         }
36418     },
36419
36420     clearMonitor : function(){
36421         Roo.get(document).un("click", this.slideInIf, this);
36422     },
36423
36424     // these names are backwards but not changed for compat
36425     slideOut : function(){
36426         if(this.isSlid || this.el.hasActiveFx()){
36427             return;
36428         }
36429         this.isSlid = true;
36430         if(this.collapseBtn){
36431             this.collapseBtn.hide();
36432         }
36433         this.closeBtnState = this.closeBtn.getStyle('display');
36434         this.closeBtn.hide();
36435         if(this.stickBtn){
36436             this.stickBtn.show();
36437         }
36438         this.el.show();
36439         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36440         this.beforeSlide();
36441         this.el.setStyle("z-index", 10001);
36442         this.el.slideIn(this.getSlideAnchor(), {
36443             callback: function(){
36444                 this.afterSlide();
36445                 this.initAutoHide();
36446                 Roo.get(document).on("click", this.slideInIf, this);
36447                 this.fireEvent("slideshow", this);
36448             },
36449             scope: this,
36450             block: true
36451         });
36452     },
36453
36454     afterSlideIn : function(){
36455         this.clearAutoHide();
36456         this.isSlid = false;
36457         this.clearMonitor();
36458         this.el.setStyle("z-index", "");
36459         if(this.collapseBtn){
36460             this.collapseBtn.show();
36461         }
36462         this.closeBtn.setStyle('display', this.closeBtnState);
36463         if(this.stickBtn){
36464             this.stickBtn.hide();
36465         }
36466         this.fireEvent("slidehide", this);
36467     },
36468
36469     slideIn : function(cb){
36470         if(!this.isSlid || this.el.hasActiveFx()){
36471             Roo.callback(cb);
36472             return;
36473         }
36474         this.isSlid = false;
36475         this.beforeSlide();
36476         this.el.slideOut(this.getSlideAnchor(), {
36477             callback: function(){
36478                 this.el.setLeftTop(-10000, -10000);
36479                 this.afterSlide();
36480                 this.afterSlideIn();
36481                 Roo.callback(cb);
36482             },
36483             scope: this,
36484             block: true
36485         });
36486     },
36487     
36488     slideInIf : function(e){
36489         if(!e.within(this.el)){
36490             this.slideIn();
36491         }
36492     },
36493
36494     animateCollapse : function(){
36495         this.beforeSlide();
36496         this.el.setStyle("z-index", 20000);
36497         var anchor = this.getSlideAnchor();
36498         this.el.slideOut(anchor, {
36499             callback : function(){
36500                 this.el.setStyle("z-index", "");
36501                 this.collapsedEl.slideIn(anchor, {duration:.3});
36502                 this.afterSlide();
36503                 this.el.setLocation(-10000,-10000);
36504                 this.el.hide();
36505                 this.fireEvent("collapsed", this);
36506             },
36507             scope: this,
36508             block: true
36509         });
36510     },
36511
36512     animateExpand : function(){
36513         this.beforeSlide();
36514         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36515         this.el.setStyle("z-index", 20000);
36516         this.collapsedEl.hide({
36517             duration:.1
36518         });
36519         this.el.slideIn(this.getSlideAnchor(), {
36520             callback : function(){
36521                 this.el.setStyle("z-index", "");
36522                 this.afterSlide();
36523                 if(this.split){
36524                     this.split.el.show();
36525                 }
36526                 this.fireEvent("invalidated", this);
36527                 this.fireEvent("expanded", this);
36528             },
36529             scope: this,
36530             block: true
36531         });
36532     },
36533
36534     anchors : {
36535         "west" : "left",
36536         "east" : "right",
36537         "north" : "top",
36538         "south" : "bottom"
36539     },
36540
36541     sanchors : {
36542         "west" : "l",
36543         "east" : "r",
36544         "north" : "t",
36545         "south" : "b"
36546     },
36547
36548     canchors : {
36549         "west" : "tl-tr",
36550         "east" : "tr-tl",
36551         "north" : "tl-bl",
36552         "south" : "bl-tl"
36553     },
36554
36555     getAnchor : function(){
36556         return this.anchors[this.position];
36557     },
36558
36559     getCollapseAnchor : function(){
36560         return this.canchors[this.position];
36561     },
36562
36563     getSlideAnchor : function(){
36564         return this.sanchors[this.position];
36565     },
36566
36567     getAlignAdj : function(){
36568         var cm = this.cmargins;
36569         switch(this.position){
36570             case "west":
36571                 return [0, 0];
36572             break;
36573             case "east":
36574                 return [0, 0];
36575             break;
36576             case "north":
36577                 return [0, 0];
36578             break;
36579             case "south":
36580                 return [0, 0];
36581             break;
36582         }
36583     },
36584
36585     getExpandAdj : function(){
36586         var c = this.collapsedEl, cm = this.cmargins;
36587         switch(this.position){
36588             case "west":
36589                 return [-(cm.right+c.getWidth()+cm.left), 0];
36590             break;
36591             case "east":
36592                 return [cm.right+c.getWidth()+cm.left, 0];
36593             break;
36594             case "north":
36595                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36596             break;
36597             case "south":
36598                 return [0, cm.top+cm.bottom+c.getHeight()];
36599             break;
36600         }
36601     }
36602 });/*
36603  * Based on:
36604  * Ext JS Library 1.1.1
36605  * Copyright(c) 2006-2007, Ext JS, LLC.
36606  *
36607  * Originally Released Under LGPL - original licence link has changed is not relivant.
36608  *
36609  * Fork - LGPL
36610  * <script type="text/javascript">
36611  */
36612 /*
36613  * These classes are private internal classes
36614  */
36615 Roo.bootstrap.layout.Center = function(config){
36616     config.region = "center";
36617     Roo.bootstrap.layout.Region.call(this, config);
36618     this.visible = true;
36619     this.minWidth = config.minWidth || 20;
36620     this.minHeight = config.minHeight || 20;
36621 };
36622
36623 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36624     hide : function(){
36625         // center panel can't be hidden
36626     },
36627     
36628     show : function(){
36629         // center panel can't be hidden
36630     },
36631     
36632     getMinWidth: function(){
36633         return this.minWidth;
36634     },
36635     
36636     getMinHeight: function(){
36637         return this.minHeight;
36638     }
36639 });
36640
36641
36642
36643
36644  
36645
36646
36647
36648
36649
36650 Roo.bootstrap.layout.North = function(config)
36651 {
36652     config.region = 'north';
36653     config.cursor = 'n-resize';
36654     
36655     Roo.bootstrap.layout.Split.call(this, config);
36656     
36657     
36658     if(this.split){
36659         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36660         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36661         this.split.el.addClass("roo-layout-split-v");
36662     }
36663     var size = config.initialSize || config.height;
36664     if(typeof size != "undefined"){
36665         this.el.setHeight(size);
36666     }
36667 };
36668 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36669 {
36670     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36671     
36672     
36673     
36674     getBox : function(){
36675         if(this.collapsed){
36676             return this.collapsedEl.getBox();
36677         }
36678         var box = this.el.getBox();
36679         if(this.split){
36680             box.height += this.split.el.getHeight();
36681         }
36682         return box;
36683     },
36684     
36685     updateBox : function(box){
36686         if(this.split && !this.collapsed){
36687             box.height -= this.split.el.getHeight();
36688             this.split.el.setLeft(box.x);
36689             this.split.el.setTop(box.y+box.height);
36690             this.split.el.setWidth(box.width);
36691         }
36692         if(this.collapsed){
36693             this.updateBody(box.width, null);
36694         }
36695         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36696     }
36697 });
36698
36699
36700
36701
36702
36703 Roo.bootstrap.layout.South = function(config){
36704     config.region = 'south';
36705     config.cursor = 's-resize';
36706     Roo.bootstrap.layout.Split.call(this, config);
36707     if(this.split){
36708         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36709         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36710         this.split.el.addClass("roo-layout-split-v");
36711     }
36712     var size = config.initialSize || config.height;
36713     if(typeof size != "undefined"){
36714         this.el.setHeight(size);
36715     }
36716 };
36717
36718 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36719     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36720     getBox : function(){
36721         if(this.collapsed){
36722             return this.collapsedEl.getBox();
36723         }
36724         var box = this.el.getBox();
36725         if(this.split){
36726             var sh = this.split.el.getHeight();
36727             box.height += sh;
36728             box.y -= sh;
36729         }
36730         return box;
36731     },
36732     
36733     updateBox : function(box){
36734         if(this.split && !this.collapsed){
36735             var sh = this.split.el.getHeight();
36736             box.height -= sh;
36737             box.y += sh;
36738             this.split.el.setLeft(box.x);
36739             this.split.el.setTop(box.y-sh);
36740             this.split.el.setWidth(box.width);
36741         }
36742         if(this.collapsed){
36743             this.updateBody(box.width, null);
36744         }
36745         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36746     }
36747 });
36748
36749 Roo.bootstrap.layout.East = function(config){
36750     config.region = "east";
36751     config.cursor = "e-resize";
36752     Roo.bootstrap.layout.Split.call(this, config);
36753     if(this.split){
36754         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36755         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36756         this.split.el.addClass("roo-layout-split-h");
36757     }
36758     var size = config.initialSize || config.width;
36759     if(typeof size != "undefined"){
36760         this.el.setWidth(size);
36761     }
36762 };
36763 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36764     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36765     getBox : function(){
36766         if(this.collapsed){
36767             return this.collapsedEl.getBox();
36768         }
36769         var box = this.el.getBox();
36770         if(this.split){
36771             var sw = this.split.el.getWidth();
36772             box.width += sw;
36773             box.x -= sw;
36774         }
36775         return box;
36776     },
36777
36778     updateBox : function(box){
36779         if(this.split && !this.collapsed){
36780             var sw = this.split.el.getWidth();
36781             box.width -= sw;
36782             this.split.el.setLeft(box.x);
36783             this.split.el.setTop(box.y);
36784             this.split.el.setHeight(box.height);
36785             box.x += sw;
36786         }
36787         if(this.collapsed){
36788             this.updateBody(null, box.height);
36789         }
36790         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36791     }
36792 });
36793
36794 Roo.bootstrap.layout.West = function(config){
36795     config.region = "west";
36796     config.cursor = "w-resize";
36797     
36798     Roo.bootstrap.layout.Split.call(this, config);
36799     if(this.split){
36800         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36801         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36802         this.split.el.addClass("roo-layout-split-h");
36803     }
36804     
36805 };
36806 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36807     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36808     
36809     onRender: function(ctr, pos)
36810     {
36811         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36812         var size = this.config.initialSize || this.config.width;
36813         if(typeof size != "undefined"){
36814             this.el.setWidth(size);
36815         }
36816     },
36817     
36818     getBox : function(){
36819         if(this.collapsed){
36820             return this.collapsedEl.getBox();
36821         }
36822         var box = this.el.getBox();
36823         if(this.split){
36824             box.width += this.split.el.getWidth();
36825         }
36826         return box;
36827     },
36828     
36829     updateBox : function(box){
36830         if(this.split && !this.collapsed){
36831             var sw = this.split.el.getWidth();
36832             box.width -= sw;
36833             this.split.el.setLeft(box.x+box.width);
36834             this.split.el.setTop(box.y);
36835             this.split.el.setHeight(box.height);
36836         }
36837         if(this.collapsed){
36838             this.updateBody(null, box.height);
36839         }
36840         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36841     }
36842 });
36843 Roo.namespace("Roo.bootstrap.panel");/*
36844  * Based on:
36845  * Ext JS Library 1.1.1
36846  * Copyright(c) 2006-2007, Ext JS, LLC.
36847  *
36848  * Originally Released Under LGPL - original licence link has changed is not relivant.
36849  *
36850  * Fork - LGPL
36851  * <script type="text/javascript">
36852  */
36853 /**
36854  * @class Roo.ContentPanel
36855  * @extends Roo.util.Observable
36856  * A basic ContentPanel element.
36857  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36858  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36859  * @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
36860  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36861  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36862  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36863  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36864  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36865  * @cfg {String} title          The title for this panel
36866  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36867  * @cfg {String} url            Calls {@link #setUrl} with this value
36868  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36869  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36870  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36871  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36872  * @cfg {Boolean} badges render the badges
36873
36874  * @constructor
36875  * Create a new ContentPanel.
36876  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36877  * @param {String/Object} config A string to set only the title or a config object
36878  * @param {String} content (optional) Set the HTML content for this panel
36879  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36880  */
36881 Roo.bootstrap.panel.Content = function( config){
36882     
36883     this.tpl = config.tpl || false;
36884     
36885     var el = config.el;
36886     var content = config.content;
36887
36888     if(config.autoCreate){ // xtype is available if this is called from factory
36889         el = Roo.id();
36890     }
36891     this.el = Roo.get(el);
36892     if(!this.el && config && config.autoCreate){
36893         if(typeof config.autoCreate == "object"){
36894             if(!config.autoCreate.id){
36895                 config.autoCreate.id = config.id||el;
36896             }
36897             this.el = Roo.DomHelper.append(document.body,
36898                         config.autoCreate, true);
36899         }else{
36900             var elcfg =  {   tag: "div",
36901                             cls: "roo-layout-inactive-content",
36902                             id: config.id||el
36903                             };
36904             if (config.html) {
36905                 elcfg.html = config.html;
36906                 
36907             }
36908                         
36909             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36910         }
36911     } 
36912     this.closable = false;
36913     this.loaded = false;
36914     this.active = false;
36915    
36916       
36917     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36918         
36919         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36920         
36921         this.wrapEl = this.el; //this.el.wrap();
36922         var ti = [];
36923         if (config.toolbar.items) {
36924             ti = config.toolbar.items ;
36925             delete config.toolbar.items ;
36926         }
36927         
36928         var nitems = [];
36929         this.toolbar.render(this.wrapEl, 'before');
36930         for(var i =0;i < ti.length;i++) {
36931           //  Roo.log(['add child', items[i]]);
36932             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36933         }
36934         this.toolbar.items = nitems;
36935         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36936         delete config.toolbar;
36937         
36938     }
36939     /*
36940     // xtype created footer. - not sure if will work as we normally have to render first..
36941     if (this.footer && !this.footer.el && this.footer.xtype) {
36942         if (!this.wrapEl) {
36943             this.wrapEl = this.el.wrap();
36944         }
36945     
36946         this.footer.container = this.wrapEl.createChild();
36947          
36948         this.footer = Roo.factory(this.footer, Roo);
36949         
36950     }
36951     */
36952     
36953      if(typeof config == "string"){
36954         this.title = config;
36955     }else{
36956         Roo.apply(this, config);
36957     }
36958     
36959     if(this.resizeEl){
36960         this.resizeEl = Roo.get(this.resizeEl, true);
36961     }else{
36962         this.resizeEl = this.el;
36963     }
36964     // handle view.xtype
36965     
36966  
36967     
36968     
36969     this.addEvents({
36970         /**
36971          * @event activate
36972          * Fires when this panel is activated. 
36973          * @param {Roo.ContentPanel} this
36974          */
36975         "activate" : true,
36976         /**
36977          * @event deactivate
36978          * Fires when this panel is activated. 
36979          * @param {Roo.ContentPanel} this
36980          */
36981         "deactivate" : true,
36982
36983         /**
36984          * @event resize
36985          * Fires when this panel is resized if fitToFrame is true.
36986          * @param {Roo.ContentPanel} this
36987          * @param {Number} width The width after any component adjustments
36988          * @param {Number} height The height after any component adjustments
36989          */
36990         "resize" : true,
36991         
36992          /**
36993          * @event render
36994          * Fires when this tab is created
36995          * @param {Roo.ContentPanel} this
36996          */
36997         "render" : true
36998         
36999         
37000         
37001     });
37002     
37003
37004     
37005     
37006     if(this.autoScroll){
37007         this.resizeEl.setStyle("overflow", "auto");
37008     } else {
37009         // fix randome scrolling
37010         //this.el.on('scroll', function() {
37011         //    Roo.log('fix random scolling');
37012         //    this.scrollTo('top',0); 
37013         //});
37014     }
37015     content = content || this.content;
37016     if(content){
37017         this.setContent(content);
37018     }
37019     if(config && config.url){
37020         this.setUrl(this.url, this.params, this.loadOnce);
37021     }
37022     
37023     
37024     
37025     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37026     
37027     if (this.view && typeof(this.view.xtype) != 'undefined') {
37028         this.view.el = this.el.appendChild(document.createElement("div"));
37029         this.view = Roo.factory(this.view); 
37030         this.view.render  &&  this.view.render(false, '');  
37031     }
37032     
37033     
37034     this.fireEvent('render', this);
37035 };
37036
37037 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37038     
37039     tabTip : '',
37040     
37041     setRegion : function(region){
37042         this.region = region;
37043         this.setActiveClass(region && !this.background);
37044     },
37045     
37046     
37047     setActiveClass: function(state)
37048     {
37049         if(state){
37050            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37051            this.el.setStyle('position','relative');
37052         }else{
37053            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37054            this.el.setStyle('position', 'absolute');
37055         } 
37056     },
37057     
37058     /**
37059      * Returns the toolbar for this Panel if one was configured. 
37060      * @return {Roo.Toolbar} 
37061      */
37062     getToolbar : function(){
37063         return this.toolbar;
37064     },
37065     
37066     setActiveState : function(active)
37067     {
37068         this.active = active;
37069         this.setActiveClass(active);
37070         if(!active){
37071             if(this.fireEvent("deactivate", this) === false){
37072                 return false;
37073             }
37074             return true;
37075         }
37076         this.fireEvent("activate", this);
37077         return true;
37078     },
37079     /**
37080      * Updates this panel's element
37081      * @param {String} content The new content
37082      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37083     */
37084     setContent : function(content, loadScripts){
37085         this.el.update(content, loadScripts);
37086     },
37087
37088     ignoreResize : function(w, h){
37089         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37090             return true;
37091         }else{
37092             this.lastSize = {width: w, height: h};
37093             return false;
37094         }
37095     },
37096     /**
37097      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37098      * @return {Roo.UpdateManager} The UpdateManager
37099      */
37100     getUpdateManager : function(){
37101         return this.el.getUpdateManager();
37102     },
37103      /**
37104      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37105      * @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:
37106 <pre><code>
37107 panel.load({
37108     url: "your-url.php",
37109     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37110     callback: yourFunction,
37111     scope: yourObject, //(optional scope)
37112     discardUrl: false,
37113     nocache: false,
37114     text: "Loading...",
37115     timeout: 30,
37116     scripts: false
37117 });
37118 </code></pre>
37119      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37120      * 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.
37121      * @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}
37122      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37123      * @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.
37124      * @return {Roo.ContentPanel} this
37125      */
37126     load : function(){
37127         var um = this.el.getUpdateManager();
37128         um.update.apply(um, arguments);
37129         return this;
37130     },
37131
37132
37133     /**
37134      * 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.
37135      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37136      * @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)
37137      * @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)
37138      * @return {Roo.UpdateManager} The UpdateManager
37139      */
37140     setUrl : function(url, params, loadOnce){
37141         if(this.refreshDelegate){
37142             this.removeListener("activate", this.refreshDelegate);
37143         }
37144         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37145         this.on("activate", this.refreshDelegate);
37146         return this.el.getUpdateManager();
37147     },
37148     
37149     _handleRefresh : function(url, params, loadOnce){
37150         if(!loadOnce || !this.loaded){
37151             var updater = this.el.getUpdateManager();
37152             updater.update(url, params, this._setLoaded.createDelegate(this));
37153         }
37154     },
37155     
37156     _setLoaded : function(){
37157         this.loaded = true;
37158     }, 
37159     
37160     /**
37161      * Returns this panel's id
37162      * @return {String} 
37163      */
37164     getId : function(){
37165         return this.el.id;
37166     },
37167     
37168     /** 
37169      * Returns this panel's element - used by regiosn to add.
37170      * @return {Roo.Element} 
37171      */
37172     getEl : function(){
37173         return this.wrapEl || this.el;
37174     },
37175     
37176    
37177     
37178     adjustForComponents : function(width, height)
37179     {
37180         //Roo.log('adjustForComponents ');
37181         if(this.resizeEl != this.el){
37182             width -= this.el.getFrameWidth('lr');
37183             height -= this.el.getFrameWidth('tb');
37184         }
37185         if(this.toolbar){
37186             var te = this.toolbar.getEl();
37187             te.setWidth(width);
37188             height -= te.getHeight();
37189         }
37190         if(this.footer){
37191             var te = this.footer.getEl();
37192             te.setWidth(width);
37193             height -= te.getHeight();
37194         }
37195         
37196         
37197         if(this.adjustments){
37198             width += this.adjustments[0];
37199             height += this.adjustments[1];
37200         }
37201         return {"width": width, "height": height};
37202     },
37203     
37204     setSize : function(width, height){
37205         if(this.fitToFrame && !this.ignoreResize(width, height)){
37206             if(this.fitContainer && this.resizeEl != this.el){
37207                 this.el.setSize(width, height);
37208             }
37209             var size = this.adjustForComponents(width, height);
37210             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37211             this.fireEvent('resize', this, size.width, size.height);
37212         }
37213     },
37214     
37215     /**
37216      * Returns this panel's title
37217      * @return {String} 
37218      */
37219     getTitle : function(){
37220         
37221         if (typeof(this.title) != 'object') {
37222             return this.title;
37223         }
37224         
37225         var t = '';
37226         for (var k in this.title) {
37227             if (!this.title.hasOwnProperty(k)) {
37228                 continue;
37229             }
37230             
37231             if (k.indexOf('-') >= 0) {
37232                 var s = k.split('-');
37233                 for (var i = 0; i<s.length; i++) {
37234                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37235                 }
37236             } else {
37237                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37238             }
37239         }
37240         return t;
37241     },
37242     
37243     /**
37244      * Set this panel's title
37245      * @param {String} title
37246      */
37247     setTitle : function(title){
37248         this.title = title;
37249         if(this.region){
37250             this.region.updatePanelTitle(this, title);
37251         }
37252     },
37253     
37254     /**
37255      * Returns true is this panel was configured to be closable
37256      * @return {Boolean} 
37257      */
37258     isClosable : function(){
37259         return this.closable;
37260     },
37261     
37262     beforeSlide : function(){
37263         this.el.clip();
37264         this.resizeEl.clip();
37265     },
37266     
37267     afterSlide : function(){
37268         this.el.unclip();
37269         this.resizeEl.unclip();
37270     },
37271     
37272     /**
37273      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37274      *   Will fail silently if the {@link #setUrl} method has not been called.
37275      *   This does not activate the panel, just updates its content.
37276      */
37277     refresh : function(){
37278         if(this.refreshDelegate){
37279            this.loaded = false;
37280            this.refreshDelegate();
37281         }
37282     },
37283     
37284     /**
37285      * Destroys this panel
37286      */
37287     destroy : function(){
37288         this.el.removeAllListeners();
37289         var tempEl = document.createElement("span");
37290         tempEl.appendChild(this.el.dom);
37291         tempEl.innerHTML = "";
37292         this.el.remove();
37293         this.el = null;
37294     },
37295     
37296     /**
37297      * form - if the content panel contains a form - this is a reference to it.
37298      * @type {Roo.form.Form}
37299      */
37300     form : false,
37301     /**
37302      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37303      *    This contains a reference to it.
37304      * @type {Roo.View}
37305      */
37306     view : false,
37307     
37308       /**
37309      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37310      * <pre><code>
37311
37312 layout.addxtype({
37313        xtype : 'Form',
37314        items: [ .... ]
37315    }
37316 );
37317
37318 </code></pre>
37319      * @param {Object} cfg Xtype definition of item to add.
37320      */
37321     
37322     
37323     getChildContainer: function () {
37324         return this.getEl();
37325     }
37326     
37327     
37328     /*
37329         var  ret = new Roo.factory(cfg);
37330         return ret;
37331         
37332         
37333         // add form..
37334         if (cfg.xtype.match(/^Form$/)) {
37335             
37336             var el;
37337             //if (this.footer) {
37338             //    el = this.footer.container.insertSibling(false, 'before');
37339             //} else {
37340                 el = this.el.createChild();
37341             //}
37342
37343             this.form = new  Roo.form.Form(cfg);
37344             
37345             
37346             if ( this.form.allItems.length) {
37347                 this.form.render(el.dom);
37348             }
37349             return this.form;
37350         }
37351         // should only have one of theses..
37352         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37353             // views.. should not be just added - used named prop 'view''
37354             
37355             cfg.el = this.el.appendChild(document.createElement("div"));
37356             // factory?
37357             
37358             var ret = new Roo.factory(cfg);
37359              
37360              ret.render && ret.render(false, ''); // render blank..
37361             this.view = ret;
37362             return ret;
37363         }
37364         return false;
37365     }
37366     \*/
37367 });
37368  
37369 /**
37370  * @class Roo.bootstrap.panel.Grid
37371  * @extends Roo.bootstrap.panel.Content
37372  * @constructor
37373  * Create a new GridPanel.
37374  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37375  * @param {Object} config A the config object
37376   
37377  */
37378
37379
37380
37381 Roo.bootstrap.panel.Grid = function(config)
37382 {
37383     
37384       
37385     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37386         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37387
37388     config.el = this.wrapper;
37389     //this.el = this.wrapper;
37390     
37391       if (config.container) {
37392         // ctor'ed from a Border/panel.grid
37393         
37394         
37395         this.wrapper.setStyle("overflow", "hidden");
37396         this.wrapper.addClass('roo-grid-container');
37397
37398     }
37399     
37400     
37401     if(config.toolbar){
37402         var tool_el = this.wrapper.createChild();    
37403         this.toolbar = Roo.factory(config.toolbar);
37404         var ti = [];
37405         if (config.toolbar.items) {
37406             ti = config.toolbar.items ;
37407             delete config.toolbar.items ;
37408         }
37409         
37410         var nitems = [];
37411         this.toolbar.render(tool_el);
37412         for(var i =0;i < ti.length;i++) {
37413           //  Roo.log(['add child', items[i]]);
37414             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37415         }
37416         this.toolbar.items = nitems;
37417         
37418         delete config.toolbar;
37419     }
37420     
37421     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37422     config.grid.scrollBody = true;;
37423     config.grid.monitorWindowResize = false; // turn off autosizing
37424     config.grid.autoHeight = false;
37425     config.grid.autoWidth = false;
37426     
37427     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37428     
37429     if (config.background) {
37430         // render grid on panel activation (if panel background)
37431         this.on('activate', function(gp) {
37432             if (!gp.grid.rendered) {
37433                 gp.grid.render(this.wrapper);
37434                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37435             }
37436         });
37437             
37438     } else {
37439         this.grid.render(this.wrapper);
37440         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37441
37442     }
37443     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37444     // ??? needed ??? config.el = this.wrapper;
37445     
37446     
37447     
37448   
37449     // xtype created footer. - not sure if will work as we normally have to render first..
37450     if (this.footer && !this.footer.el && this.footer.xtype) {
37451         
37452         var ctr = this.grid.getView().getFooterPanel(true);
37453         this.footer.dataSource = this.grid.dataSource;
37454         this.footer = Roo.factory(this.footer, Roo);
37455         this.footer.render(ctr);
37456         
37457     }
37458     
37459     
37460     
37461     
37462      
37463 };
37464
37465 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37466     getId : function(){
37467         return this.grid.id;
37468     },
37469     
37470     /**
37471      * Returns the grid for this panel
37472      * @return {Roo.bootstrap.Table} 
37473      */
37474     getGrid : function(){
37475         return this.grid;    
37476     },
37477     
37478     setSize : function(width, height){
37479         if(!this.ignoreResize(width, height)){
37480             var grid = this.grid;
37481             var size = this.adjustForComponents(width, height);
37482             var gridel = grid.getGridEl();
37483             gridel.setSize(size.width, size.height);
37484             /*
37485             var thd = grid.getGridEl().select('thead',true).first();
37486             var tbd = grid.getGridEl().select('tbody', true).first();
37487             if (tbd) {
37488                 tbd.setSize(width, height - thd.getHeight());
37489             }
37490             */
37491             grid.autoSize();
37492         }
37493     },
37494      
37495     
37496     
37497     beforeSlide : function(){
37498         this.grid.getView().scroller.clip();
37499     },
37500     
37501     afterSlide : function(){
37502         this.grid.getView().scroller.unclip();
37503     },
37504     
37505     destroy : function(){
37506         this.grid.destroy();
37507         delete this.grid;
37508         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37509     }
37510 });
37511
37512 /**
37513  * @class Roo.bootstrap.panel.Nest
37514  * @extends Roo.bootstrap.panel.Content
37515  * @constructor
37516  * Create a new Panel, that can contain a layout.Border.
37517  * 
37518  * 
37519  * @param {Roo.BorderLayout} layout The layout for this panel
37520  * @param {String/Object} config A string to set only the title or a config object
37521  */
37522 Roo.bootstrap.panel.Nest = function(config)
37523 {
37524     // construct with only one argument..
37525     /* FIXME - implement nicer consturctors
37526     if (layout.layout) {
37527         config = layout;
37528         layout = config.layout;
37529         delete config.layout;
37530     }
37531     if (layout.xtype && !layout.getEl) {
37532         // then layout needs constructing..
37533         layout = Roo.factory(layout, Roo);
37534     }
37535     */
37536     
37537     config.el =  config.layout.getEl();
37538     
37539     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37540     
37541     config.layout.monitorWindowResize = false; // turn off autosizing
37542     this.layout = config.layout;
37543     this.layout.getEl().addClass("roo-layout-nested-layout");
37544     
37545     
37546     
37547     
37548 };
37549
37550 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37551
37552     setSize : function(width, height){
37553         if(!this.ignoreResize(width, height)){
37554             var size = this.adjustForComponents(width, height);
37555             var el = this.layout.getEl();
37556             if (size.height < 1) {
37557                 el.setWidth(size.width);   
37558             } else {
37559                 el.setSize(size.width, size.height);
37560             }
37561             var touch = el.dom.offsetWidth;
37562             this.layout.layout();
37563             // ie requires a double layout on the first pass
37564             if(Roo.isIE && !this.initialized){
37565                 this.initialized = true;
37566                 this.layout.layout();
37567             }
37568         }
37569     },
37570     
37571     // activate all subpanels if not currently active..
37572     
37573     setActiveState : function(active){
37574         this.active = active;
37575         this.setActiveClass(active);
37576         
37577         if(!active){
37578             this.fireEvent("deactivate", this);
37579             return;
37580         }
37581         
37582         this.fireEvent("activate", this);
37583         // not sure if this should happen before or after..
37584         if (!this.layout) {
37585             return; // should not happen..
37586         }
37587         var reg = false;
37588         for (var r in this.layout.regions) {
37589             reg = this.layout.getRegion(r);
37590             if (reg.getActivePanel()) {
37591                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37592                 reg.setActivePanel(reg.getActivePanel());
37593                 continue;
37594             }
37595             if (!reg.panels.length) {
37596                 continue;
37597             }
37598             reg.showPanel(reg.getPanel(0));
37599         }
37600         
37601         
37602         
37603         
37604     },
37605     
37606     /**
37607      * Returns the nested BorderLayout for this panel
37608      * @return {Roo.BorderLayout} 
37609      */
37610     getLayout : function(){
37611         return this.layout;
37612     },
37613     
37614      /**
37615      * Adds a xtype elements to the layout of the nested panel
37616      * <pre><code>
37617
37618 panel.addxtype({
37619        xtype : 'ContentPanel',
37620        region: 'west',
37621        items: [ .... ]
37622    }
37623 );
37624
37625 panel.addxtype({
37626         xtype : 'NestedLayoutPanel',
37627         region: 'west',
37628         layout: {
37629            center: { },
37630            west: { }   
37631         },
37632         items : [ ... list of content panels or nested layout panels.. ]
37633    }
37634 );
37635 </code></pre>
37636      * @param {Object} cfg Xtype definition of item to add.
37637      */
37638     addxtype : function(cfg) {
37639         return this.layout.addxtype(cfg);
37640     
37641     }
37642 });        /*
37643  * Based on:
37644  * Ext JS Library 1.1.1
37645  * Copyright(c) 2006-2007, Ext JS, LLC.
37646  *
37647  * Originally Released Under LGPL - original licence link has changed is not relivant.
37648  *
37649  * Fork - LGPL
37650  * <script type="text/javascript">
37651  */
37652 /**
37653  * @class Roo.TabPanel
37654  * @extends Roo.util.Observable
37655  * A lightweight tab container.
37656  * <br><br>
37657  * Usage:
37658  * <pre><code>
37659 // basic tabs 1, built from existing content
37660 var tabs = new Roo.TabPanel("tabs1");
37661 tabs.addTab("script", "View Script");
37662 tabs.addTab("markup", "View Markup");
37663 tabs.activate("script");
37664
37665 // more advanced tabs, built from javascript
37666 var jtabs = new Roo.TabPanel("jtabs");
37667 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37668
37669 // set up the UpdateManager
37670 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37671 var updater = tab2.getUpdateManager();
37672 updater.setDefaultUrl("ajax1.htm");
37673 tab2.on('activate', updater.refresh, updater, true);
37674
37675 // Use setUrl for Ajax loading
37676 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37677 tab3.setUrl("ajax2.htm", null, true);
37678
37679 // Disabled tab
37680 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37681 tab4.disable();
37682
37683 jtabs.activate("jtabs-1");
37684  * </code></pre>
37685  * @constructor
37686  * Create a new TabPanel.
37687  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37688  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37689  */
37690 Roo.bootstrap.panel.Tabs = function(config){
37691     /**
37692     * The container element for this TabPanel.
37693     * @type Roo.Element
37694     */
37695     this.el = Roo.get(config.el);
37696     delete config.el;
37697     if(config){
37698         if(typeof config == "boolean"){
37699             this.tabPosition = config ? "bottom" : "top";
37700         }else{
37701             Roo.apply(this, config);
37702         }
37703     }
37704     
37705     if(this.tabPosition == "bottom"){
37706         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37707         this.el.addClass("roo-tabs-bottom");
37708     }
37709     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37710     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37711     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37712     if(Roo.isIE){
37713         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37714     }
37715     if(this.tabPosition != "bottom"){
37716         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37717          * @type Roo.Element
37718          */
37719         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37720         this.el.addClass("roo-tabs-top");
37721     }
37722     this.items = [];
37723
37724     this.bodyEl.setStyle("position", "relative");
37725
37726     this.active = null;
37727     this.activateDelegate = this.activate.createDelegate(this);
37728
37729     this.addEvents({
37730         /**
37731          * @event tabchange
37732          * Fires when the active tab changes
37733          * @param {Roo.TabPanel} this
37734          * @param {Roo.TabPanelItem} activePanel The new active tab
37735          */
37736         "tabchange": true,
37737         /**
37738          * @event beforetabchange
37739          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37740          * @param {Roo.TabPanel} this
37741          * @param {Object} e Set cancel to true on this object to cancel the tab change
37742          * @param {Roo.TabPanelItem} tab The tab being changed to
37743          */
37744         "beforetabchange" : true
37745     });
37746
37747     Roo.EventManager.onWindowResize(this.onResize, this);
37748     this.cpad = this.el.getPadding("lr");
37749     this.hiddenCount = 0;
37750
37751
37752     // toolbar on the tabbar support...
37753     if (this.toolbar) {
37754         alert("no toolbar support yet");
37755         this.toolbar  = false;
37756         /*
37757         var tcfg = this.toolbar;
37758         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37759         this.toolbar = new Roo.Toolbar(tcfg);
37760         if (Roo.isSafari) {
37761             var tbl = tcfg.container.child('table', true);
37762             tbl.setAttribute('width', '100%');
37763         }
37764         */
37765         
37766     }
37767    
37768
37769
37770     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37771 };
37772
37773 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37774     /*
37775      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37776      */
37777     tabPosition : "top",
37778     /*
37779      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37780      */
37781     currentTabWidth : 0,
37782     /*
37783      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37784      */
37785     minTabWidth : 40,
37786     /*
37787      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37788      */
37789     maxTabWidth : 250,
37790     /*
37791      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37792      */
37793     preferredTabWidth : 175,
37794     /*
37795      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37796      */
37797     resizeTabs : false,
37798     /*
37799      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37800      */
37801     monitorResize : true,
37802     /*
37803      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37804      */
37805     toolbar : false,
37806
37807     /**
37808      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37809      * @param {String} id The id of the div to use <b>or create</b>
37810      * @param {String} text The text for the tab
37811      * @param {String} content (optional) Content to put in the TabPanelItem body
37812      * @param {Boolean} closable (optional) True to create a close icon on the tab
37813      * @return {Roo.TabPanelItem} The created TabPanelItem
37814      */
37815     addTab : function(id, text, content, closable, tpl)
37816     {
37817         var item = new Roo.bootstrap.panel.TabItem({
37818             panel: this,
37819             id : id,
37820             text : text,
37821             closable : closable,
37822             tpl : tpl
37823         });
37824         this.addTabItem(item);
37825         if(content){
37826             item.setContent(content);
37827         }
37828         return item;
37829     },
37830
37831     /**
37832      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37833      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37834      * @return {Roo.TabPanelItem}
37835      */
37836     getTab : function(id){
37837         return this.items[id];
37838     },
37839
37840     /**
37841      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37842      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37843      */
37844     hideTab : function(id){
37845         var t = this.items[id];
37846         if(!t.isHidden()){
37847            t.setHidden(true);
37848            this.hiddenCount++;
37849            this.autoSizeTabs();
37850         }
37851     },
37852
37853     /**
37854      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37855      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37856      */
37857     unhideTab : function(id){
37858         var t = this.items[id];
37859         if(t.isHidden()){
37860            t.setHidden(false);
37861            this.hiddenCount--;
37862            this.autoSizeTabs();
37863         }
37864     },
37865
37866     /**
37867      * Adds an existing {@link Roo.TabPanelItem}.
37868      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37869      */
37870     addTabItem : function(item){
37871         this.items[item.id] = item;
37872         this.items.push(item);
37873       //  if(this.resizeTabs){
37874     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37875   //         this.autoSizeTabs();
37876 //        }else{
37877 //            item.autoSize();
37878        // }
37879     },
37880
37881     /**
37882      * Removes a {@link Roo.TabPanelItem}.
37883      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37884      */
37885     removeTab : function(id){
37886         var items = this.items;
37887         var tab = items[id];
37888         if(!tab) { return; }
37889         var index = items.indexOf(tab);
37890         if(this.active == tab && items.length > 1){
37891             var newTab = this.getNextAvailable(index);
37892             if(newTab) {
37893                 newTab.activate();
37894             }
37895         }
37896         this.stripEl.dom.removeChild(tab.pnode.dom);
37897         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37898             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37899         }
37900         items.splice(index, 1);
37901         delete this.items[tab.id];
37902         tab.fireEvent("close", tab);
37903         tab.purgeListeners();
37904         this.autoSizeTabs();
37905     },
37906
37907     getNextAvailable : function(start){
37908         var items = this.items;
37909         var index = start;
37910         // look for a next tab that will slide over to
37911         // replace the one being removed
37912         while(index < items.length){
37913             var item = items[++index];
37914             if(item && !item.isHidden()){
37915                 return item;
37916             }
37917         }
37918         // if one isn't found select the previous tab (on the left)
37919         index = start;
37920         while(index >= 0){
37921             var item = items[--index];
37922             if(item && !item.isHidden()){
37923                 return item;
37924             }
37925         }
37926         return null;
37927     },
37928
37929     /**
37930      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37931      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37932      */
37933     disableTab : function(id){
37934         var tab = this.items[id];
37935         if(tab && this.active != tab){
37936             tab.disable();
37937         }
37938     },
37939
37940     /**
37941      * Enables a {@link Roo.TabPanelItem} that is disabled.
37942      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37943      */
37944     enableTab : function(id){
37945         var tab = this.items[id];
37946         tab.enable();
37947     },
37948
37949     /**
37950      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37951      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37952      * @return {Roo.TabPanelItem} The TabPanelItem.
37953      */
37954     activate : function(id){
37955         var tab = this.items[id];
37956         if(!tab){
37957             return null;
37958         }
37959         if(tab == this.active || tab.disabled){
37960             return tab;
37961         }
37962         var e = {};
37963         this.fireEvent("beforetabchange", this, e, tab);
37964         if(e.cancel !== true && !tab.disabled){
37965             if(this.active){
37966                 this.active.hide();
37967             }
37968             this.active = this.items[id];
37969             this.active.show();
37970             this.fireEvent("tabchange", this, this.active);
37971         }
37972         return tab;
37973     },
37974
37975     /**
37976      * Gets the active {@link Roo.TabPanelItem}.
37977      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37978      */
37979     getActiveTab : function(){
37980         return this.active;
37981     },
37982
37983     /**
37984      * Updates the tab body element to fit the height of the container element
37985      * for overflow scrolling
37986      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37987      */
37988     syncHeight : function(targetHeight){
37989         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37990         var bm = this.bodyEl.getMargins();
37991         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37992         this.bodyEl.setHeight(newHeight);
37993         return newHeight;
37994     },
37995
37996     onResize : function(){
37997         if(this.monitorResize){
37998             this.autoSizeTabs();
37999         }
38000     },
38001
38002     /**
38003      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38004      */
38005     beginUpdate : function(){
38006         this.updating = true;
38007     },
38008
38009     /**
38010      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38011      */
38012     endUpdate : function(){
38013         this.updating = false;
38014         this.autoSizeTabs();
38015     },
38016
38017     /**
38018      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38019      */
38020     autoSizeTabs : function(){
38021         var count = this.items.length;
38022         var vcount = count - this.hiddenCount;
38023         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38024             return;
38025         }
38026         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38027         var availWidth = Math.floor(w / vcount);
38028         var b = this.stripBody;
38029         if(b.getWidth() > w){
38030             var tabs = this.items;
38031             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38032             if(availWidth < this.minTabWidth){
38033                 /*if(!this.sleft){    // incomplete scrolling code
38034                     this.createScrollButtons();
38035                 }
38036                 this.showScroll();
38037                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38038             }
38039         }else{
38040             if(this.currentTabWidth < this.preferredTabWidth){
38041                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38042             }
38043         }
38044     },
38045
38046     /**
38047      * Returns the number of tabs in this TabPanel.
38048      * @return {Number}
38049      */
38050      getCount : function(){
38051          return this.items.length;
38052      },
38053
38054     /**
38055      * Resizes all the tabs to the passed width
38056      * @param {Number} The new width
38057      */
38058     setTabWidth : function(width){
38059         this.currentTabWidth = width;
38060         for(var i = 0, len = this.items.length; i < len; i++) {
38061                 if(!this.items[i].isHidden()) {
38062                 this.items[i].setWidth(width);
38063             }
38064         }
38065     },
38066
38067     /**
38068      * Destroys this TabPanel
38069      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38070      */
38071     destroy : function(removeEl){
38072         Roo.EventManager.removeResizeListener(this.onResize, this);
38073         for(var i = 0, len = this.items.length; i < len; i++){
38074             this.items[i].purgeListeners();
38075         }
38076         if(removeEl === true){
38077             this.el.update("");
38078             this.el.remove();
38079         }
38080     },
38081     
38082     createStrip : function(container)
38083     {
38084         var strip = document.createElement("nav");
38085         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38086         container.appendChild(strip);
38087         return strip;
38088     },
38089     
38090     createStripList : function(strip)
38091     {
38092         // div wrapper for retard IE
38093         // returns the "tr" element.
38094         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38095         //'<div class="x-tabs-strip-wrap">'+
38096           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38097           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38098         return strip.firstChild; //.firstChild.firstChild.firstChild;
38099     },
38100     createBody : function(container)
38101     {
38102         var body = document.createElement("div");
38103         Roo.id(body, "tab-body");
38104         //Roo.fly(body).addClass("x-tabs-body");
38105         Roo.fly(body).addClass("tab-content");
38106         container.appendChild(body);
38107         return body;
38108     },
38109     createItemBody :function(bodyEl, id){
38110         var body = Roo.getDom(id);
38111         if(!body){
38112             body = document.createElement("div");
38113             body.id = id;
38114         }
38115         //Roo.fly(body).addClass("x-tabs-item-body");
38116         Roo.fly(body).addClass("tab-pane");
38117          bodyEl.insertBefore(body, bodyEl.firstChild);
38118         return body;
38119     },
38120     /** @private */
38121     createStripElements :  function(stripEl, text, closable, tpl)
38122     {
38123         var td = document.createElement("li"); // was td..
38124         
38125         
38126         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38127         
38128         
38129         stripEl.appendChild(td);
38130         /*if(closable){
38131             td.className = "x-tabs-closable";
38132             if(!this.closeTpl){
38133                 this.closeTpl = new Roo.Template(
38134                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38135                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38136                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38137                 );
38138             }
38139             var el = this.closeTpl.overwrite(td, {"text": text});
38140             var close = el.getElementsByTagName("div")[0];
38141             var inner = el.getElementsByTagName("em")[0];
38142             return {"el": el, "close": close, "inner": inner};
38143         } else {
38144         */
38145         // not sure what this is..
38146 //            if(!this.tabTpl){
38147                 //this.tabTpl = new Roo.Template(
38148                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38149                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38150                 //);
38151 //                this.tabTpl = new Roo.Template(
38152 //                   '<a href="#">' +
38153 //                   '<span unselectable="on"' +
38154 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38155 //                            ' >{text}</span></a>'
38156 //                );
38157 //                
38158 //            }
38159
38160
38161             var template = tpl || this.tabTpl || false;
38162             
38163             if(!template){
38164                 
38165                 template = new Roo.Template(
38166                    '<a href="#">' +
38167                    '<span unselectable="on"' +
38168                             (this.disableTooltips ? '' : ' title="{text}"') +
38169                             ' >{text}</span></a>'
38170                 );
38171             }
38172             
38173             switch (typeof(template)) {
38174                 case 'object' :
38175                     break;
38176                 case 'string' :
38177                     template = new Roo.Template(template);
38178                     break;
38179                 default :
38180                     break;
38181             }
38182             
38183             var el = template.overwrite(td, {"text": text});
38184             
38185             var inner = el.getElementsByTagName("span")[0];
38186             
38187             return {"el": el, "inner": inner};
38188             
38189     }
38190         
38191     
38192 });
38193
38194 /**
38195  * @class Roo.TabPanelItem
38196  * @extends Roo.util.Observable
38197  * Represents an individual item (tab plus body) in a TabPanel.
38198  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38199  * @param {String} id The id of this TabPanelItem
38200  * @param {String} text The text for the tab of this TabPanelItem
38201  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38202  */
38203 Roo.bootstrap.panel.TabItem = function(config){
38204     /**
38205      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38206      * @type Roo.TabPanel
38207      */
38208     this.tabPanel = config.panel;
38209     /**
38210      * The id for this TabPanelItem
38211      * @type String
38212      */
38213     this.id = config.id;
38214     /** @private */
38215     this.disabled = false;
38216     /** @private */
38217     this.text = config.text;
38218     /** @private */
38219     this.loaded = false;
38220     this.closable = config.closable;
38221
38222     /**
38223      * The body element for this TabPanelItem.
38224      * @type Roo.Element
38225      */
38226     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38227     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38228     this.bodyEl.setStyle("display", "block");
38229     this.bodyEl.setStyle("zoom", "1");
38230     //this.hideAction();
38231
38232     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38233     /** @private */
38234     this.el = Roo.get(els.el);
38235     this.inner = Roo.get(els.inner, true);
38236     this.textEl = Roo.get(this.el.dom.firstChild, true);
38237     this.pnode = Roo.get(els.el.parentNode, true);
38238 //    this.el.on("mousedown", this.onTabMouseDown, this);
38239     this.el.on("click", this.onTabClick, this);
38240     /** @private */
38241     if(config.closable){
38242         var c = Roo.get(els.close, true);
38243         c.dom.title = this.closeText;
38244         c.addClassOnOver("close-over");
38245         c.on("click", this.closeClick, this);
38246      }
38247
38248     this.addEvents({
38249          /**
38250          * @event activate
38251          * Fires when this tab becomes the active tab.
38252          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38253          * @param {Roo.TabPanelItem} this
38254          */
38255         "activate": true,
38256         /**
38257          * @event beforeclose
38258          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38259          * @param {Roo.TabPanelItem} this
38260          * @param {Object} e Set cancel to true on this object to cancel the close.
38261          */
38262         "beforeclose": true,
38263         /**
38264          * @event close
38265          * Fires when this tab is closed.
38266          * @param {Roo.TabPanelItem} this
38267          */
38268          "close": true,
38269         /**
38270          * @event deactivate
38271          * Fires when this tab is no longer the active tab.
38272          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38273          * @param {Roo.TabPanelItem} this
38274          */
38275          "deactivate" : true
38276     });
38277     this.hidden = false;
38278
38279     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38280 };
38281
38282 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38283            {
38284     purgeListeners : function(){
38285        Roo.util.Observable.prototype.purgeListeners.call(this);
38286        this.el.removeAllListeners();
38287     },
38288     /**
38289      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38290      */
38291     show : function(){
38292         this.pnode.addClass("active");
38293         this.showAction();
38294         if(Roo.isOpera){
38295             this.tabPanel.stripWrap.repaint();
38296         }
38297         this.fireEvent("activate", this.tabPanel, this);
38298     },
38299
38300     /**
38301      * Returns true if this tab is the active tab.
38302      * @return {Boolean}
38303      */
38304     isActive : function(){
38305         return this.tabPanel.getActiveTab() == this;
38306     },
38307
38308     /**
38309      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38310      */
38311     hide : function(){
38312         this.pnode.removeClass("active");
38313         this.hideAction();
38314         this.fireEvent("deactivate", this.tabPanel, this);
38315     },
38316
38317     hideAction : function(){
38318         this.bodyEl.hide();
38319         this.bodyEl.setStyle("position", "absolute");
38320         this.bodyEl.setLeft("-20000px");
38321         this.bodyEl.setTop("-20000px");
38322     },
38323
38324     showAction : function(){
38325         this.bodyEl.setStyle("position", "relative");
38326         this.bodyEl.setTop("");
38327         this.bodyEl.setLeft("");
38328         this.bodyEl.show();
38329     },
38330
38331     /**
38332      * Set the tooltip for the tab.
38333      * @param {String} tooltip The tab's tooltip
38334      */
38335     setTooltip : function(text){
38336         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38337             this.textEl.dom.qtip = text;
38338             this.textEl.dom.removeAttribute('title');
38339         }else{
38340             this.textEl.dom.title = text;
38341         }
38342     },
38343
38344     onTabClick : function(e){
38345         e.preventDefault();
38346         this.tabPanel.activate(this.id);
38347     },
38348
38349     onTabMouseDown : function(e){
38350         e.preventDefault();
38351         this.tabPanel.activate(this.id);
38352     },
38353 /*
38354     getWidth : function(){
38355         return this.inner.getWidth();
38356     },
38357
38358     setWidth : function(width){
38359         var iwidth = width - this.pnode.getPadding("lr");
38360         this.inner.setWidth(iwidth);
38361         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38362         this.pnode.setWidth(width);
38363     },
38364 */
38365     /**
38366      * Show or hide the tab
38367      * @param {Boolean} hidden True to hide or false to show.
38368      */
38369     setHidden : function(hidden){
38370         this.hidden = hidden;
38371         this.pnode.setStyle("display", hidden ? "none" : "");
38372     },
38373
38374     /**
38375      * Returns true if this tab is "hidden"
38376      * @return {Boolean}
38377      */
38378     isHidden : function(){
38379         return this.hidden;
38380     },
38381
38382     /**
38383      * Returns the text for this tab
38384      * @return {String}
38385      */
38386     getText : function(){
38387         return this.text;
38388     },
38389     /*
38390     autoSize : function(){
38391         //this.el.beginMeasure();
38392         this.textEl.setWidth(1);
38393         /*
38394          *  #2804 [new] Tabs in Roojs
38395          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38396          */
38397         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38398         //this.el.endMeasure();
38399     //},
38400
38401     /**
38402      * Sets the text for the tab (Note: this also sets the tooltip text)
38403      * @param {String} text The tab's text and tooltip
38404      */
38405     setText : function(text){
38406         this.text = text;
38407         this.textEl.update(text);
38408         this.setTooltip(text);
38409         //if(!this.tabPanel.resizeTabs){
38410         //    this.autoSize();
38411         //}
38412     },
38413     /**
38414      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38415      */
38416     activate : function(){
38417         this.tabPanel.activate(this.id);
38418     },
38419
38420     /**
38421      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38422      */
38423     disable : function(){
38424         if(this.tabPanel.active != this){
38425             this.disabled = true;
38426             this.pnode.addClass("disabled");
38427         }
38428     },
38429
38430     /**
38431      * Enables this TabPanelItem if it was previously disabled.
38432      */
38433     enable : function(){
38434         this.disabled = false;
38435         this.pnode.removeClass("disabled");
38436     },
38437
38438     /**
38439      * Sets the content for this TabPanelItem.
38440      * @param {String} content The content
38441      * @param {Boolean} loadScripts true to look for and load scripts
38442      */
38443     setContent : function(content, loadScripts){
38444         this.bodyEl.update(content, loadScripts);
38445     },
38446
38447     /**
38448      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38449      * @return {Roo.UpdateManager} The UpdateManager
38450      */
38451     getUpdateManager : function(){
38452         return this.bodyEl.getUpdateManager();
38453     },
38454
38455     /**
38456      * Set a URL to be used to load the content for this TabPanelItem.
38457      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38458      * @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)
38459      * @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)
38460      * @return {Roo.UpdateManager} The UpdateManager
38461      */
38462     setUrl : function(url, params, loadOnce){
38463         if(this.refreshDelegate){
38464             this.un('activate', this.refreshDelegate);
38465         }
38466         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38467         this.on("activate", this.refreshDelegate);
38468         return this.bodyEl.getUpdateManager();
38469     },
38470
38471     /** @private */
38472     _handleRefresh : function(url, params, loadOnce){
38473         if(!loadOnce || !this.loaded){
38474             var updater = this.bodyEl.getUpdateManager();
38475             updater.update(url, params, this._setLoaded.createDelegate(this));
38476         }
38477     },
38478
38479     /**
38480      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38481      *   Will fail silently if the setUrl method has not been called.
38482      *   This does not activate the panel, just updates its content.
38483      */
38484     refresh : function(){
38485         if(this.refreshDelegate){
38486            this.loaded = false;
38487            this.refreshDelegate();
38488         }
38489     },
38490
38491     /** @private */
38492     _setLoaded : function(){
38493         this.loaded = true;
38494     },
38495
38496     /** @private */
38497     closeClick : function(e){
38498         var o = {};
38499         e.stopEvent();
38500         this.fireEvent("beforeclose", this, o);
38501         if(o.cancel !== true){
38502             this.tabPanel.removeTab(this.id);
38503         }
38504     },
38505     /**
38506      * The text displayed in the tooltip for the close icon.
38507      * @type String
38508      */
38509     closeText : "Close this tab"
38510 });
38511 /**
38512 *    This script refer to:
38513 *    Title: International Telephone Input
38514 *    Author: Jack O'Connor
38515 *    Code version:  v12.1.12
38516 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38517 **/
38518
38519 Roo.bootstrap.PhoneInputData = function() {
38520     var d = [
38521       [
38522         "Afghanistan (‫افغانستان‬‎)",
38523         "af",
38524         "93"
38525       ],
38526       [
38527         "Albania (Shqipëri)",
38528         "al",
38529         "355"
38530       ],
38531       [
38532         "Algeria (‫الجزائر‬‎)",
38533         "dz",
38534         "213"
38535       ],
38536       [
38537         "American Samoa",
38538         "as",
38539         "1684"
38540       ],
38541       [
38542         "Andorra",
38543         "ad",
38544         "376"
38545       ],
38546       [
38547         "Angola",
38548         "ao",
38549         "244"
38550       ],
38551       [
38552         "Anguilla",
38553         "ai",
38554         "1264"
38555       ],
38556       [
38557         "Antigua and Barbuda",
38558         "ag",
38559         "1268"
38560       ],
38561       [
38562         "Argentina",
38563         "ar",
38564         "54"
38565       ],
38566       [
38567         "Armenia (Հայաստան)",
38568         "am",
38569         "374"
38570       ],
38571       [
38572         "Aruba",
38573         "aw",
38574         "297"
38575       ],
38576       [
38577         "Australia",
38578         "au",
38579         "61",
38580         0
38581       ],
38582       [
38583         "Austria (Österreich)",
38584         "at",
38585         "43"
38586       ],
38587       [
38588         "Azerbaijan (Azərbaycan)",
38589         "az",
38590         "994"
38591       ],
38592       [
38593         "Bahamas",
38594         "bs",
38595         "1242"
38596       ],
38597       [
38598         "Bahrain (‫البحرين‬‎)",
38599         "bh",
38600         "973"
38601       ],
38602       [
38603         "Bangladesh (বাংলাদেশ)",
38604         "bd",
38605         "880"
38606       ],
38607       [
38608         "Barbados",
38609         "bb",
38610         "1246"
38611       ],
38612       [
38613         "Belarus (Беларусь)",
38614         "by",
38615         "375"
38616       ],
38617       [
38618         "Belgium (België)",
38619         "be",
38620         "32"
38621       ],
38622       [
38623         "Belize",
38624         "bz",
38625         "501"
38626       ],
38627       [
38628         "Benin (Bénin)",
38629         "bj",
38630         "229"
38631       ],
38632       [
38633         "Bermuda",
38634         "bm",
38635         "1441"
38636       ],
38637       [
38638         "Bhutan (འབྲུག)",
38639         "bt",
38640         "975"
38641       ],
38642       [
38643         "Bolivia",
38644         "bo",
38645         "591"
38646       ],
38647       [
38648         "Bosnia and Herzegovina (Босна и Херцеговина)",
38649         "ba",
38650         "387"
38651       ],
38652       [
38653         "Botswana",
38654         "bw",
38655         "267"
38656       ],
38657       [
38658         "Brazil (Brasil)",
38659         "br",
38660         "55"
38661       ],
38662       [
38663         "British Indian Ocean Territory",
38664         "io",
38665         "246"
38666       ],
38667       [
38668         "British Virgin Islands",
38669         "vg",
38670         "1284"
38671       ],
38672       [
38673         "Brunei",
38674         "bn",
38675         "673"
38676       ],
38677       [
38678         "Bulgaria (България)",
38679         "bg",
38680         "359"
38681       ],
38682       [
38683         "Burkina Faso",
38684         "bf",
38685         "226"
38686       ],
38687       [
38688         "Burundi (Uburundi)",
38689         "bi",
38690         "257"
38691       ],
38692       [
38693         "Cambodia (កម្ពុជា)",
38694         "kh",
38695         "855"
38696       ],
38697       [
38698         "Cameroon (Cameroun)",
38699         "cm",
38700         "237"
38701       ],
38702       [
38703         "Canada",
38704         "ca",
38705         "1",
38706         1,
38707         ["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"]
38708       ],
38709       [
38710         "Cape Verde (Kabu Verdi)",
38711         "cv",
38712         "238"
38713       ],
38714       [
38715         "Caribbean Netherlands",
38716         "bq",
38717         "599",
38718         1
38719       ],
38720       [
38721         "Cayman Islands",
38722         "ky",
38723         "1345"
38724       ],
38725       [
38726         "Central African Republic (République centrafricaine)",
38727         "cf",
38728         "236"
38729       ],
38730       [
38731         "Chad (Tchad)",
38732         "td",
38733         "235"
38734       ],
38735       [
38736         "Chile",
38737         "cl",
38738         "56"
38739       ],
38740       [
38741         "China (中国)",
38742         "cn",
38743         "86"
38744       ],
38745       [
38746         "Christmas Island",
38747         "cx",
38748         "61",
38749         2
38750       ],
38751       [
38752         "Cocos (Keeling) Islands",
38753         "cc",
38754         "61",
38755         1
38756       ],
38757       [
38758         "Colombia",
38759         "co",
38760         "57"
38761       ],
38762       [
38763         "Comoros (‫جزر القمر‬‎)",
38764         "km",
38765         "269"
38766       ],
38767       [
38768         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38769         "cd",
38770         "243"
38771       ],
38772       [
38773         "Congo (Republic) (Congo-Brazzaville)",
38774         "cg",
38775         "242"
38776       ],
38777       [
38778         "Cook Islands",
38779         "ck",
38780         "682"
38781       ],
38782       [
38783         "Costa Rica",
38784         "cr",
38785         "506"
38786       ],
38787       [
38788         "Côte d’Ivoire",
38789         "ci",
38790         "225"
38791       ],
38792       [
38793         "Croatia (Hrvatska)",
38794         "hr",
38795         "385"
38796       ],
38797       [
38798         "Cuba",
38799         "cu",
38800         "53"
38801       ],
38802       [
38803         "Curaçao",
38804         "cw",
38805         "599",
38806         0
38807       ],
38808       [
38809         "Cyprus (Κύπρος)",
38810         "cy",
38811         "357"
38812       ],
38813       [
38814         "Czech Republic (Česká republika)",
38815         "cz",
38816         "420"
38817       ],
38818       [
38819         "Denmark (Danmark)",
38820         "dk",
38821         "45"
38822       ],
38823       [
38824         "Djibouti",
38825         "dj",
38826         "253"
38827       ],
38828       [
38829         "Dominica",
38830         "dm",
38831         "1767"
38832       ],
38833       [
38834         "Dominican Republic (República Dominicana)",
38835         "do",
38836         "1",
38837         2,
38838         ["809", "829", "849"]
38839       ],
38840       [
38841         "Ecuador",
38842         "ec",
38843         "593"
38844       ],
38845       [
38846         "Egypt (‫مصر‬‎)",
38847         "eg",
38848         "20"
38849       ],
38850       [
38851         "El Salvador",
38852         "sv",
38853         "503"
38854       ],
38855       [
38856         "Equatorial Guinea (Guinea Ecuatorial)",
38857         "gq",
38858         "240"
38859       ],
38860       [
38861         "Eritrea",
38862         "er",
38863         "291"
38864       ],
38865       [
38866         "Estonia (Eesti)",
38867         "ee",
38868         "372"
38869       ],
38870       [
38871         "Ethiopia",
38872         "et",
38873         "251"
38874       ],
38875       [
38876         "Falkland Islands (Islas Malvinas)",
38877         "fk",
38878         "500"
38879       ],
38880       [
38881         "Faroe Islands (Føroyar)",
38882         "fo",
38883         "298"
38884       ],
38885       [
38886         "Fiji",
38887         "fj",
38888         "679"
38889       ],
38890       [
38891         "Finland (Suomi)",
38892         "fi",
38893         "358",
38894         0
38895       ],
38896       [
38897         "France",
38898         "fr",
38899         "33"
38900       ],
38901       [
38902         "French Guiana (Guyane française)",
38903         "gf",
38904         "594"
38905       ],
38906       [
38907         "French Polynesia (Polynésie française)",
38908         "pf",
38909         "689"
38910       ],
38911       [
38912         "Gabon",
38913         "ga",
38914         "241"
38915       ],
38916       [
38917         "Gambia",
38918         "gm",
38919         "220"
38920       ],
38921       [
38922         "Georgia (საქართველო)",
38923         "ge",
38924         "995"
38925       ],
38926       [
38927         "Germany (Deutschland)",
38928         "de",
38929         "49"
38930       ],
38931       [
38932         "Ghana (Gaana)",
38933         "gh",
38934         "233"
38935       ],
38936       [
38937         "Gibraltar",
38938         "gi",
38939         "350"
38940       ],
38941       [
38942         "Greece (Ελλάδα)",
38943         "gr",
38944         "30"
38945       ],
38946       [
38947         "Greenland (Kalaallit Nunaat)",
38948         "gl",
38949         "299"
38950       ],
38951       [
38952         "Grenada",
38953         "gd",
38954         "1473"
38955       ],
38956       [
38957         "Guadeloupe",
38958         "gp",
38959         "590",
38960         0
38961       ],
38962       [
38963         "Guam",
38964         "gu",
38965         "1671"
38966       ],
38967       [
38968         "Guatemala",
38969         "gt",
38970         "502"
38971       ],
38972       [
38973         "Guernsey",
38974         "gg",
38975         "44",
38976         1
38977       ],
38978       [
38979         "Guinea (Guinée)",
38980         "gn",
38981         "224"
38982       ],
38983       [
38984         "Guinea-Bissau (Guiné Bissau)",
38985         "gw",
38986         "245"
38987       ],
38988       [
38989         "Guyana",
38990         "gy",
38991         "592"
38992       ],
38993       [
38994         "Haiti",
38995         "ht",
38996         "509"
38997       ],
38998       [
38999         "Honduras",
39000         "hn",
39001         "504"
39002       ],
39003       [
39004         "Hong Kong (香港)",
39005         "hk",
39006         "852"
39007       ],
39008       [
39009         "Hungary (Magyarország)",
39010         "hu",
39011         "36"
39012       ],
39013       [
39014         "Iceland (Ísland)",
39015         "is",
39016         "354"
39017       ],
39018       [
39019         "India (भारत)",
39020         "in",
39021         "91"
39022       ],
39023       [
39024         "Indonesia",
39025         "id",
39026         "62"
39027       ],
39028       [
39029         "Iran (‫ایران‬‎)",
39030         "ir",
39031         "98"
39032       ],
39033       [
39034         "Iraq (‫العراق‬‎)",
39035         "iq",
39036         "964"
39037       ],
39038       [
39039         "Ireland",
39040         "ie",
39041         "353"
39042       ],
39043       [
39044         "Isle of Man",
39045         "im",
39046         "44",
39047         2
39048       ],
39049       [
39050         "Israel (‫ישראל‬‎)",
39051         "il",
39052         "972"
39053       ],
39054       [
39055         "Italy (Italia)",
39056         "it",
39057         "39",
39058         0
39059       ],
39060       [
39061         "Jamaica",
39062         "jm",
39063         "1876"
39064       ],
39065       [
39066         "Japan (日本)",
39067         "jp",
39068         "81"
39069       ],
39070       [
39071         "Jersey",
39072         "je",
39073         "44",
39074         3
39075       ],
39076       [
39077         "Jordan (‫الأردن‬‎)",
39078         "jo",
39079         "962"
39080       ],
39081       [
39082         "Kazakhstan (Казахстан)",
39083         "kz",
39084         "7",
39085         1
39086       ],
39087       [
39088         "Kenya",
39089         "ke",
39090         "254"
39091       ],
39092       [
39093         "Kiribati",
39094         "ki",
39095         "686"
39096       ],
39097       [
39098         "Kosovo",
39099         "xk",
39100         "383"
39101       ],
39102       [
39103         "Kuwait (‫الكويت‬‎)",
39104         "kw",
39105         "965"
39106       ],
39107       [
39108         "Kyrgyzstan (Кыргызстан)",
39109         "kg",
39110         "996"
39111       ],
39112       [
39113         "Laos (ລາວ)",
39114         "la",
39115         "856"
39116       ],
39117       [
39118         "Latvia (Latvija)",
39119         "lv",
39120         "371"
39121       ],
39122       [
39123         "Lebanon (‫لبنان‬‎)",
39124         "lb",
39125         "961"
39126       ],
39127       [
39128         "Lesotho",
39129         "ls",
39130         "266"
39131       ],
39132       [
39133         "Liberia",
39134         "lr",
39135         "231"
39136       ],
39137       [
39138         "Libya (‫ليبيا‬‎)",
39139         "ly",
39140         "218"
39141       ],
39142       [
39143         "Liechtenstein",
39144         "li",
39145         "423"
39146       ],
39147       [
39148         "Lithuania (Lietuva)",
39149         "lt",
39150         "370"
39151       ],
39152       [
39153         "Luxembourg",
39154         "lu",
39155         "352"
39156       ],
39157       [
39158         "Macau (澳門)",
39159         "mo",
39160         "853"
39161       ],
39162       [
39163         "Macedonia (FYROM) (Македонија)",
39164         "mk",
39165         "389"
39166       ],
39167       [
39168         "Madagascar (Madagasikara)",
39169         "mg",
39170         "261"
39171       ],
39172       [
39173         "Malawi",
39174         "mw",
39175         "265"
39176       ],
39177       [
39178         "Malaysia",
39179         "my",
39180         "60"
39181       ],
39182       [
39183         "Maldives",
39184         "mv",
39185         "960"
39186       ],
39187       [
39188         "Mali",
39189         "ml",
39190         "223"
39191       ],
39192       [
39193         "Malta",
39194         "mt",
39195         "356"
39196       ],
39197       [
39198         "Marshall Islands",
39199         "mh",
39200         "692"
39201       ],
39202       [
39203         "Martinique",
39204         "mq",
39205         "596"
39206       ],
39207       [
39208         "Mauritania (‫موريتانيا‬‎)",
39209         "mr",
39210         "222"
39211       ],
39212       [
39213         "Mauritius (Moris)",
39214         "mu",
39215         "230"
39216       ],
39217       [
39218         "Mayotte",
39219         "yt",
39220         "262",
39221         1
39222       ],
39223       [
39224         "Mexico (México)",
39225         "mx",
39226         "52"
39227       ],
39228       [
39229         "Micronesia",
39230         "fm",
39231         "691"
39232       ],
39233       [
39234         "Moldova (Republica Moldova)",
39235         "md",
39236         "373"
39237       ],
39238       [
39239         "Monaco",
39240         "mc",
39241         "377"
39242       ],
39243       [
39244         "Mongolia (Монгол)",
39245         "mn",
39246         "976"
39247       ],
39248       [
39249         "Montenegro (Crna Gora)",
39250         "me",
39251         "382"
39252       ],
39253       [
39254         "Montserrat",
39255         "ms",
39256         "1664"
39257       ],
39258       [
39259         "Morocco (‫المغرب‬‎)",
39260         "ma",
39261         "212",
39262         0
39263       ],
39264       [
39265         "Mozambique (Moçambique)",
39266         "mz",
39267         "258"
39268       ],
39269       [
39270         "Myanmar (Burma) (မြန်မာ)",
39271         "mm",
39272         "95"
39273       ],
39274       [
39275         "Namibia (Namibië)",
39276         "na",
39277         "264"
39278       ],
39279       [
39280         "Nauru",
39281         "nr",
39282         "674"
39283       ],
39284       [
39285         "Nepal (नेपाल)",
39286         "np",
39287         "977"
39288       ],
39289       [
39290         "Netherlands (Nederland)",
39291         "nl",
39292         "31"
39293       ],
39294       [
39295         "New Caledonia (Nouvelle-Calédonie)",
39296         "nc",
39297         "687"
39298       ],
39299       [
39300         "New Zealand",
39301         "nz",
39302         "64"
39303       ],
39304       [
39305         "Nicaragua",
39306         "ni",
39307         "505"
39308       ],
39309       [
39310         "Niger (Nijar)",
39311         "ne",
39312         "227"
39313       ],
39314       [
39315         "Nigeria",
39316         "ng",
39317         "234"
39318       ],
39319       [
39320         "Niue",
39321         "nu",
39322         "683"
39323       ],
39324       [
39325         "Norfolk Island",
39326         "nf",
39327         "672"
39328       ],
39329       [
39330         "North Korea (조선 민주주의 인민 공화국)",
39331         "kp",
39332         "850"
39333       ],
39334       [
39335         "Northern Mariana Islands",
39336         "mp",
39337         "1670"
39338       ],
39339       [
39340         "Norway (Norge)",
39341         "no",
39342         "47",
39343         0
39344       ],
39345       [
39346         "Oman (‫عُمان‬‎)",
39347         "om",
39348         "968"
39349       ],
39350       [
39351         "Pakistan (‫پاکستان‬‎)",
39352         "pk",
39353         "92"
39354       ],
39355       [
39356         "Palau",
39357         "pw",
39358         "680"
39359       ],
39360       [
39361         "Palestine (‫فلسطين‬‎)",
39362         "ps",
39363         "970"
39364       ],
39365       [
39366         "Panama (Panamá)",
39367         "pa",
39368         "507"
39369       ],
39370       [
39371         "Papua New Guinea",
39372         "pg",
39373         "675"
39374       ],
39375       [
39376         "Paraguay",
39377         "py",
39378         "595"
39379       ],
39380       [
39381         "Peru (Perú)",
39382         "pe",
39383         "51"
39384       ],
39385       [
39386         "Philippines",
39387         "ph",
39388         "63"
39389       ],
39390       [
39391         "Poland (Polska)",
39392         "pl",
39393         "48"
39394       ],
39395       [
39396         "Portugal",
39397         "pt",
39398         "351"
39399       ],
39400       [
39401         "Puerto Rico",
39402         "pr",
39403         "1",
39404         3,
39405         ["787", "939"]
39406       ],
39407       [
39408         "Qatar (‫قطر‬‎)",
39409         "qa",
39410         "974"
39411       ],
39412       [
39413         "Réunion (La Réunion)",
39414         "re",
39415         "262",
39416         0
39417       ],
39418       [
39419         "Romania (România)",
39420         "ro",
39421         "40"
39422       ],
39423       [
39424         "Russia (Россия)",
39425         "ru",
39426         "7",
39427         0
39428       ],
39429       [
39430         "Rwanda",
39431         "rw",
39432         "250"
39433       ],
39434       [
39435         "Saint Barthélemy",
39436         "bl",
39437         "590",
39438         1
39439       ],
39440       [
39441         "Saint Helena",
39442         "sh",
39443         "290"
39444       ],
39445       [
39446         "Saint Kitts and Nevis",
39447         "kn",
39448         "1869"
39449       ],
39450       [
39451         "Saint Lucia",
39452         "lc",
39453         "1758"
39454       ],
39455       [
39456         "Saint Martin (Saint-Martin (partie française))",
39457         "mf",
39458         "590",
39459         2
39460       ],
39461       [
39462         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39463         "pm",
39464         "508"
39465       ],
39466       [
39467         "Saint Vincent and the Grenadines",
39468         "vc",
39469         "1784"
39470       ],
39471       [
39472         "Samoa",
39473         "ws",
39474         "685"
39475       ],
39476       [
39477         "San Marino",
39478         "sm",
39479         "378"
39480       ],
39481       [
39482         "São Tomé and Príncipe (São Tomé e Príncipe)",
39483         "st",
39484         "239"
39485       ],
39486       [
39487         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39488         "sa",
39489         "966"
39490       ],
39491       [
39492         "Senegal (Sénégal)",
39493         "sn",
39494         "221"
39495       ],
39496       [
39497         "Serbia (Србија)",
39498         "rs",
39499         "381"
39500       ],
39501       [
39502         "Seychelles",
39503         "sc",
39504         "248"
39505       ],
39506       [
39507         "Sierra Leone",
39508         "sl",
39509         "232"
39510       ],
39511       [
39512         "Singapore",
39513         "sg",
39514         "65"
39515       ],
39516       [
39517         "Sint Maarten",
39518         "sx",
39519         "1721"
39520       ],
39521       [
39522         "Slovakia (Slovensko)",
39523         "sk",
39524         "421"
39525       ],
39526       [
39527         "Slovenia (Slovenija)",
39528         "si",
39529         "386"
39530       ],
39531       [
39532         "Solomon Islands",
39533         "sb",
39534         "677"
39535       ],
39536       [
39537         "Somalia (Soomaaliya)",
39538         "so",
39539         "252"
39540       ],
39541       [
39542         "South Africa",
39543         "za",
39544         "27"
39545       ],
39546       [
39547         "South Korea (대한민국)",
39548         "kr",
39549         "82"
39550       ],
39551       [
39552         "South Sudan (‫جنوب السودان‬‎)",
39553         "ss",
39554         "211"
39555       ],
39556       [
39557         "Spain (España)",
39558         "es",
39559         "34"
39560       ],
39561       [
39562         "Sri Lanka (ශ්‍රී ලංකාව)",
39563         "lk",
39564         "94"
39565       ],
39566       [
39567         "Sudan (‫السودان‬‎)",
39568         "sd",
39569         "249"
39570       ],
39571       [
39572         "Suriname",
39573         "sr",
39574         "597"
39575       ],
39576       [
39577         "Svalbard and Jan Mayen",
39578         "sj",
39579         "47",
39580         1
39581       ],
39582       [
39583         "Swaziland",
39584         "sz",
39585         "268"
39586       ],
39587       [
39588         "Sweden (Sverige)",
39589         "se",
39590         "46"
39591       ],
39592       [
39593         "Switzerland (Schweiz)",
39594         "ch",
39595         "41"
39596       ],
39597       [
39598         "Syria (‫سوريا‬‎)",
39599         "sy",
39600         "963"
39601       ],
39602       [
39603         "Taiwan (台灣)",
39604         "tw",
39605         "886"
39606       ],
39607       [
39608         "Tajikistan",
39609         "tj",
39610         "992"
39611       ],
39612       [
39613         "Tanzania",
39614         "tz",
39615         "255"
39616       ],
39617       [
39618         "Thailand (ไทย)",
39619         "th",
39620         "66"
39621       ],
39622       [
39623         "Timor-Leste",
39624         "tl",
39625         "670"
39626       ],
39627       [
39628         "Togo",
39629         "tg",
39630         "228"
39631       ],
39632       [
39633         "Tokelau",
39634         "tk",
39635         "690"
39636       ],
39637       [
39638         "Tonga",
39639         "to",
39640         "676"
39641       ],
39642       [
39643         "Trinidad and Tobago",
39644         "tt",
39645         "1868"
39646       ],
39647       [
39648         "Tunisia (‫تونس‬‎)",
39649         "tn",
39650         "216"
39651       ],
39652       [
39653         "Turkey (Türkiye)",
39654         "tr",
39655         "90"
39656       ],
39657       [
39658         "Turkmenistan",
39659         "tm",
39660         "993"
39661       ],
39662       [
39663         "Turks and Caicos Islands",
39664         "tc",
39665         "1649"
39666       ],
39667       [
39668         "Tuvalu",
39669         "tv",
39670         "688"
39671       ],
39672       [
39673         "U.S. Virgin Islands",
39674         "vi",
39675         "1340"
39676       ],
39677       [
39678         "Uganda",
39679         "ug",
39680         "256"
39681       ],
39682       [
39683         "Ukraine (Україна)",
39684         "ua",
39685         "380"
39686       ],
39687       [
39688         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39689         "ae",
39690         "971"
39691       ],
39692       [
39693         "United Kingdom",
39694         "gb",
39695         "44",
39696         0
39697       ],
39698       [
39699         "United States",
39700         "us",
39701         "1",
39702         0
39703       ],
39704       [
39705         "Uruguay",
39706         "uy",
39707         "598"
39708       ],
39709       [
39710         "Uzbekistan (Oʻzbekiston)",
39711         "uz",
39712         "998"
39713       ],
39714       [
39715         "Vanuatu",
39716         "vu",
39717         "678"
39718       ],
39719       [
39720         "Vatican City (Città del Vaticano)",
39721         "va",
39722         "39",
39723         1
39724       ],
39725       [
39726         "Venezuela",
39727         "ve",
39728         "58"
39729       ],
39730       [
39731         "Vietnam (Việt Nam)",
39732         "vn",
39733         "84"
39734       ],
39735       [
39736         "Wallis and Futuna (Wallis-et-Futuna)",
39737         "wf",
39738         "681"
39739       ],
39740       [
39741         "Western Sahara (‫الصحراء الغربية‬‎)",
39742         "eh",
39743         "212",
39744         1
39745       ],
39746       [
39747         "Yemen (‫اليمن‬‎)",
39748         "ye",
39749         "967"
39750       ],
39751       [
39752         "Zambia",
39753         "zm",
39754         "260"
39755       ],
39756       [
39757         "Zimbabwe",
39758         "zw",
39759         "263"
39760       ],
39761       [
39762         "Åland Islands",
39763         "ax",
39764         "358",
39765         1
39766       ]
39767   ];
39768   
39769   return d;
39770 }/**
39771 *    This script refer to:
39772 *    Title: International Telephone Input
39773 *    Author: Jack O'Connor
39774 *    Code version:  v12.1.12
39775 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39776 **/
39777
39778 /**
39779  * @class Roo.bootstrap.PhoneInput
39780  * @extends Roo.bootstrap.TriggerField
39781  * An input with International dial-code selection
39782  
39783  * @cfg {String} defaultDialCode default '+852'
39784  * @cfg {Array} preferedCountries default []
39785   
39786  * @constructor
39787  * Create a new PhoneInput.
39788  * @param {Object} config Configuration options
39789  */
39790
39791 Roo.bootstrap.PhoneInput = function(config) {
39792     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39793 };
39794
39795 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39796         
39797         listWidth: undefined,
39798         
39799         selectedClass: 'active',
39800         
39801         invalidClass : "has-warning",
39802         
39803         validClass: 'has-success',
39804         
39805         allowed: '0123456789',
39806         
39807         /**
39808          * @cfg {String} defaultDialCode The default dial code when initializing the input
39809          */
39810         defaultDialCode: '+852',
39811         
39812         /**
39813          * @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
39814          */
39815         preferedCountries: false,
39816         
39817         getAutoCreate : function()
39818         {
39819             var data = Roo.bootstrap.PhoneInputData();
39820             var align = this.labelAlign || this.parentLabelAlign();
39821             var id = Roo.id();
39822             
39823             this.allCountries = [];
39824             this.dialCodeMapping = [];
39825             
39826             for (var i = 0; i < data.length; i++) {
39827               var c = data[i];
39828               this.allCountries[i] = {
39829                 name: c[0],
39830                 iso2: c[1],
39831                 dialCode: c[2],
39832                 priority: c[3] || 0,
39833                 areaCodes: c[4] || null
39834               };
39835               this.dialCodeMapping[c[2]] = {
39836                   name: c[0],
39837                   iso2: c[1],
39838                   priority: c[3] || 0,
39839                   areaCodes: c[4] || null
39840               };
39841             }
39842             
39843             var cfg = {
39844                 cls: 'form-group',
39845                 cn: []
39846             };
39847             
39848             var input =  {
39849                 tag: 'input',
39850                 id : id,
39851                 cls : 'form-control tel-input',
39852                 autocomplete: 'new-password'
39853             };
39854             
39855             var hiddenInput = {
39856                 tag: 'input',
39857                 type: 'hidden',
39858                 cls: 'hidden-tel-input'
39859             };
39860             
39861             if (this.name) {
39862                 hiddenInput.name = this.name;
39863             }
39864             
39865             if (this.disabled) {
39866                 input.disabled = true;
39867             }
39868             
39869             var flag_container = {
39870                 tag: 'div',
39871                 cls: 'flag-box',
39872                 cn: [
39873                     {
39874                         tag: 'div',
39875                         cls: 'flag'
39876                     },
39877                     {
39878                         tag: 'div',
39879                         cls: 'caret'
39880                     }
39881                 ]
39882             };
39883             
39884             var box = {
39885                 tag: 'div',
39886                 cls: this.hasFeedback ? 'has-feedback' : '',
39887                 cn: [
39888                     hiddenInput,
39889                     input,
39890                     {
39891                         tag: 'input',
39892                         cls: 'dial-code-holder',
39893                         disabled: true
39894                     }
39895                 ]
39896             };
39897             
39898             var container = {
39899                 cls: 'roo-select2-container input-group',
39900                 cn: [
39901                     flag_container,
39902                     box
39903                 ]
39904             };
39905             
39906             if (this.fieldLabel.length) {
39907                 var indicator = {
39908                     tag: 'i',
39909                     tooltip: 'This field is required'
39910                 };
39911                 
39912                 var label = {
39913                     tag: 'label',
39914                     'for':  id,
39915                     cls: 'control-label',
39916                     cn: []
39917                 };
39918                 
39919                 var label_text = {
39920                     tag: 'span',
39921                     html: this.fieldLabel
39922                 };
39923                 
39924                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39925                 label.cn = [
39926                     indicator,
39927                     label_text
39928                 ];
39929                 
39930                 if(this.indicatorpos == 'right') {
39931                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39932                     label.cn = [
39933                         label_text,
39934                         indicator
39935                     ];
39936                 }
39937                 
39938                 if(align == 'left') {
39939                     container = {
39940                         tag: 'div',
39941                         cn: [
39942                             container
39943                         ]
39944                     };
39945                     
39946                     if(this.labelWidth > 12){
39947                         label.style = "width: " + this.labelWidth + 'px';
39948                     }
39949                     if(this.labelWidth < 13 && this.labelmd == 0){
39950                         this.labelmd = this.labelWidth;
39951                     }
39952                     if(this.labellg > 0){
39953                         label.cls += ' col-lg-' + this.labellg;
39954                         input.cls += ' col-lg-' + (12 - this.labellg);
39955                     }
39956                     if(this.labelmd > 0){
39957                         label.cls += ' col-md-' + this.labelmd;
39958                         container.cls += ' col-md-' + (12 - this.labelmd);
39959                     }
39960                     if(this.labelsm > 0){
39961                         label.cls += ' col-sm-' + this.labelsm;
39962                         container.cls += ' col-sm-' + (12 - this.labelsm);
39963                     }
39964                     if(this.labelxs > 0){
39965                         label.cls += ' col-xs-' + this.labelxs;
39966                         container.cls += ' col-xs-' + (12 - this.labelxs);
39967                     }
39968                 }
39969             }
39970             
39971             cfg.cn = [
39972                 label,
39973                 container
39974             ];
39975             
39976             var settings = this;
39977             
39978             ['xs','sm','md','lg'].map(function(size){
39979                 if (settings[size]) {
39980                     cfg.cls += ' col-' + size + '-' + settings[size];
39981                 }
39982             });
39983             
39984             this.store = new Roo.data.Store({
39985                 proxy : new Roo.data.MemoryProxy({}),
39986                 reader : new Roo.data.JsonReader({
39987                     fields : [
39988                         {
39989                             'name' : 'name',
39990                             'type' : 'string'
39991                         },
39992                         {
39993                             'name' : 'iso2',
39994                             'type' : 'string'
39995                         },
39996                         {
39997                             'name' : 'dialCode',
39998                             'type' : 'string'
39999                         },
40000                         {
40001                             'name' : 'priority',
40002                             'type' : 'string'
40003                         },
40004                         {
40005                             'name' : 'areaCodes',
40006                             'type' : 'string'
40007                         }
40008                     ]
40009                 })
40010             });
40011             
40012             if(!this.preferedCountries) {
40013                 this.preferedCountries = [
40014                     'hk',
40015                     'gb',
40016                     'us'
40017                 ];
40018             }
40019             
40020             var p = this.preferedCountries.reverse();
40021             
40022             if(p) {
40023                 for (var i = 0; i < p.length; i++) {
40024                     for (var j = 0; j < this.allCountries.length; j++) {
40025                         if(this.allCountries[j].iso2 == p[i]) {
40026                             var t = this.allCountries[j];
40027                             this.allCountries.splice(j,1);
40028                             this.allCountries.unshift(t);
40029                         }
40030                     } 
40031                 }
40032             }
40033             
40034             this.store.proxy.data = {
40035                 success: true,
40036                 data: this.allCountries
40037             };
40038             
40039             return cfg;
40040         },
40041         
40042         initEvents : function()
40043         {
40044             this.createList();
40045             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40046             
40047             this.indicator = this.indicatorEl();
40048             this.flag = this.flagEl();
40049             this.dialCodeHolder = this.dialCodeHolderEl();
40050             
40051             this.trigger = this.el.select('div.flag-box',true).first();
40052             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40053             
40054             var _this = this;
40055             
40056             (function(){
40057                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40058                 _this.list.setWidth(lw);
40059             }).defer(100);
40060             
40061             this.list.on('mouseover', this.onViewOver, this);
40062             this.list.on('mousemove', this.onViewMove, this);
40063             this.inputEl().on("keyup", this.onKeyUp, this);
40064             
40065             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40066
40067             this.view = new Roo.View(this.list, this.tpl, {
40068                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40069             });
40070             
40071             this.view.on('click', this.onViewClick, this);
40072             this.setValue(this.defaultDialCode);
40073         },
40074         
40075         onTriggerClick : function(e)
40076         {
40077             Roo.log('trigger click');
40078             if(this.disabled){
40079                 return;
40080             }
40081             
40082             if(this.isExpanded()){
40083                 this.collapse();
40084                 this.hasFocus = false;
40085             }else {
40086                 this.store.load({});
40087                 this.hasFocus = true;
40088                 this.expand();
40089             }
40090         },
40091         
40092         isExpanded : function()
40093         {
40094             return this.list.isVisible();
40095         },
40096         
40097         collapse : function()
40098         {
40099             if(!this.isExpanded()){
40100                 return;
40101             }
40102             this.list.hide();
40103             Roo.get(document).un('mousedown', this.collapseIf, this);
40104             Roo.get(document).un('mousewheel', this.collapseIf, this);
40105             this.fireEvent('collapse', this);
40106             this.validate();
40107         },
40108         
40109         expand : function()
40110         {
40111             Roo.log('expand');
40112
40113             if(this.isExpanded() || !this.hasFocus){
40114                 return;
40115             }
40116             
40117             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40118             this.list.setWidth(lw);
40119             
40120             this.list.show();
40121             this.restrictHeight();
40122             
40123             Roo.get(document).on('mousedown', this.collapseIf, this);
40124             Roo.get(document).on('mousewheel', this.collapseIf, this);
40125             
40126             this.fireEvent('expand', this);
40127         },
40128         
40129         restrictHeight : function()
40130         {
40131             this.list.alignTo(this.inputEl(), this.listAlign);
40132             this.list.alignTo(this.inputEl(), this.listAlign);
40133         },
40134         
40135         onViewOver : function(e, t)
40136         {
40137             if(this.inKeyMode){
40138                 return;
40139             }
40140             var item = this.view.findItemFromChild(t);
40141             
40142             if(item){
40143                 var index = this.view.indexOf(item);
40144                 this.select(index, false);
40145             }
40146         },
40147
40148         // private
40149         onViewClick : function(view, doFocus, el, e)
40150         {
40151             var index = this.view.getSelectedIndexes()[0];
40152             
40153             var r = this.store.getAt(index);
40154             
40155             if(r){
40156                 this.onSelect(r, index);
40157             }
40158             if(doFocus !== false && !this.blockFocus){
40159                 this.inputEl().focus();
40160             }
40161         },
40162         
40163         onViewMove : function(e, t)
40164         {
40165             this.inKeyMode = false;
40166         },
40167         
40168         select : function(index, scrollIntoView)
40169         {
40170             this.selectedIndex = index;
40171             this.view.select(index);
40172             if(scrollIntoView !== false){
40173                 var el = this.view.getNode(index);
40174                 if(el){
40175                     this.list.scrollChildIntoView(el, false);
40176                 }
40177             }
40178         },
40179         
40180         createList : function()
40181         {
40182             this.list = Roo.get(document.body).createChild({
40183                 tag: 'ul',
40184                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40185                 style: 'display:none'
40186             });
40187             
40188             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40189         },
40190         
40191         collapseIf : function(e)
40192         {
40193             var in_combo  = e.within(this.el);
40194             var in_list =  e.within(this.list);
40195             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40196             
40197             if (in_combo || in_list || is_list) {
40198                 return;
40199             }
40200             this.collapse();
40201         },
40202         
40203         onSelect : function(record, index)
40204         {
40205             if(this.fireEvent('beforeselect', this, record, index) !== false){
40206                 
40207                 this.setFlagClass(record.data.iso2);
40208                 this.setDialCode(record.data.dialCode);
40209                 this.hasFocus = false;
40210                 this.collapse();
40211                 this.fireEvent('select', this, record, index);
40212             }
40213         },
40214         
40215         flagEl : function()
40216         {
40217             var flag = this.el.select('div.flag',true).first();
40218             if(!flag){
40219                 return false;
40220             }
40221             return flag;
40222         },
40223         
40224         dialCodeHolderEl : function()
40225         {
40226             var d = this.el.select('input.dial-code-holder',true).first();
40227             if(!d){
40228                 return false;
40229             }
40230             return d;
40231         },
40232         
40233         setDialCode : function(v)
40234         {
40235             this.dialCodeHolder.dom.value = '+'+v;
40236         },
40237         
40238         setFlagClass : function(n)
40239         {
40240             this.flag.dom.className = 'flag '+n;
40241         },
40242         
40243         getValue : function()
40244         {
40245             var v = this.inputEl().getValue();
40246             if(this.dialCodeHolder) {
40247                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40248             }
40249             return v;
40250         },
40251         
40252         setValue : function(v)
40253         {
40254             var d = this.getDialCode(v);
40255             
40256             //invalid dial code
40257             if(v.length == 0 || !d || d.length == 0) {
40258                 if(this.rendered){
40259                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40260                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40261                 }
40262                 return;
40263             }
40264             
40265             //valid dial code
40266             this.setFlagClass(this.dialCodeMapping[d].iso2);
40267             this.setDialCode(d);
40268             this.inputEl().dom.value = v.replace('+'+d,'');
40269             this.hiddenEl().dom.value = this.getValue();
40270             
40271             this.validate();
40272         },
40273         
40274         getDialCode : function(v)
40275         {
40276             v = v ||  '';
40277             
40278             if (v.length == 0) {
40279                 return this.dialCodeHolder.dom.value;
40280             }
40281             
40282             var dialCode = "";
40283             if (v.charAt(0) != "+") {
40284                 return false;
40285             }
40286             var numericChars = "";
40287             for (var i = 1; i < v.length; i++) {
40288               var c = v.charAt(i);
40289               if (!isNaN(c)) {
40290                 numericChars += c;
40291                 if (this.dialCodeMapping[numericChars]) {
40292                   dialCode = v.substr(1, i);
40293                 }
40294                 if (numericChars.length == 4) {
40295                   break;
40296                 }
40297               }
40298             }
40299             return dialCode;
40300         },
40301         
40302         reset : function()
40303         {
40304             this.setValue(this.defaultDialCode);
40305             this.validate();
40306         },
40307         
40308         hiddenEl : function()
40309         {
40310             return this.el.select('input.hidden-tel-input',true).first();
40311         },
40312         
40313         onKeyUp : function(e){
40314             
40315             var k = e.getKey();
40316             var c = e.getCharCode();
40317             
40318             if(
40319                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40320                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40321             ){
40322                 e.stopEvent();
40323             }
40324             
40325             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40326             //     return;
40327             // }
40328             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40329                 e.stopEvent();
40330             }
40331             
40332             this.setValue(this.getValue());
40333         }
40334         
40335 });
40336 /**
40337  * @class Roo.bootstrap.MoneyField
40338  * @extends Roo.bootstrap.ComboBox
40339  * Bootstrap MoneyField class
40340  * 
40341  * @constructor
40342  * Create a new MoneyField.
40343  * @param {Object} config Configuration options
40344  */
40345
40346 Roo.bootstrap.MoneyField = function(config) {
40347     
40348     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40349     
40350 };
40351
40352 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40353     
40354     /**
40355      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40356      */
40357     allowDecimals : true,
40358     /**
40359      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40360      */
40361     decimalSeparator : ".",
40362     /**
40363      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40364      */
40365     decimalPrecision : 0,
40366     /**
40367      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40368      */
40369     allowNegative : true,
40370     /**
40371      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40372      */
40373     allowZero: true,
40374     /**
40375      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40376      */
40377     minValue : Number.NEGATIVE_INFINITY,
40378     /**
40379      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40380      */
40381     maxValue : Number.MAX_VALUE,
40382     /**
40383      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40384      */
40385     minText : "The minimum value for this field is {0}",
40386     /**
40387      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40388      */
40389     maxText : "The maximum value for this field is {0}",
40390     /**
40391      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40392      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40393      */
40394     nanText : "{0} is not a valid number",
40395     /**
40396      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40397      */
40398     castInt : true,
40399     /**
40400      * @cfg {String} defaults currency of the MoneyField
40401      * value should be in lkey
40402      */
40403     defaultCurrency : false,
40404     /**
40405      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40406      */
40407     thousandsDelimiter : false,
40408     
40409     
40410     inputlg : 9,
40411     inputmd : 9,
40412     inputsm : 9,
40413     inputxs : 6,
40414     
40415     store : false,
40416     
40417     getAutoCreate : function()
40418     {
40419         var align = this.labelAlign || this.parentLabelAlign();
40420         
40421         var id = Roo.id();
40422
40423         var cfg = {
40424             cls: 'form-group',
40425             cn: []
40426         };
40427
40428         var input =  {
40429             tag: 'input',
40430             id : id,
40431             cls : 'form-control roo-money-amount-input',
40432             autocomplete: 'new-password'
40433         };
40434         
40435         var hiddenInput = {
40436             tag: 'input',
40437             type: 'hidden',
40438             id: Roo.id(),
40439             cls: 'hidden-number-input'
40440         };
40441         
40442         if (this.name) {
40443             hiddenInput.name = this.name;
40444         }
40445
40446         if (this.disabled) {
40447             input.disabled = true;
40448         }
40449
40450         var clg = 12 - this.inputlg;
40451         var cmd = 12 - this.inputmd;
40452         var csm = 12 - this.inputsm;
40453         var cxs = 12 - this.inputxs;
40454         
40455         var container = {
40456             tag : 'div',
40457             cls : 'row roo-money-field',
40458             cn : [
40459                 {
40460                     tag : 'div',
40461                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40462                     cn : [
40463                         {
40464                             tag : 'div',
40465                             cls: 'roo-select2-container input-group',
40466                             cn: [
40467                                 {
40468                                     tag : 'input',
40469                                     cls : 'form-control roo-money-currency-input',
40470                                     autocomplete: 'new-password',
40471                                     readOnly : 1,
40472                                     name : this.currencyName
40473                                 },
40474                                 {
40475                                     tag :'span',
40476                                     cls : 'input-group-addon',
40477                                     cn : [
40478                                         {
40479                                             tag: 'span',
40480                                             cls: 'caret'
40481                                         }
40482                                     ]
40483                                 }
40484                             ]
40485                         }
40486                     ]
40487                 },
40488                 {
40489                     tag : 'div',
40490                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40491                     cn : [
40492                         {
40493                             tag: 'div',
40494                             cls: this.hasFeedback ? 'has-feedback' : '',
40495                             cn: [
40496                                 input
40497                             ]
40498                         }
40499                     ]
40500                 }
40501             ]
40502             
40503         };
40504         
40505         if (this.fieldLabel.length) {
40506             var indicator = {
40507                 tag: 'i',
40508                 tooltip: 'This field is required'
40509             };
40510
40511             var label = {
40512                 tag: 'label',
40513                 'for':  id,
40514                 cls: 'control-label',
40515                 cn: []
40516             };
40517
40518             var label_text = {
40519                 tag: 'span',
40520                 html: this.fieldLabel
40521             };
40522
40523             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40524             label.cn = [
40525                 indicator,
40526                 label_text
40527             ];
40528
40529             if(this.indicatorpos == 'right') {
40530                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40531                 label.cn = [
40532                     label_text,
40533                     indicator
40534                 ];
40535             }
40536
40537             if(align == 'left') {
40538                 container = {
40539                     tag: 'div',
40540                     cn: [
40541                         container
40542                     ]
40543                 };
40544
40545                 if(this.labelWidth > 12){
40546                     label.style = "width: " + this.labelWidth + 'px';
40547                 }
40548                 if(this.labelWidth < 13 && this.labelmd == 0){
40549                     this.labelmd = this.labelWidth;
40550                 }
40551                 if(this.labellg > 0){
40552                     label.cls += ' col-lg-' + this.labellg;
40553                     input.cls += ' col-lg-' + (12 - this.labellg);
40554                 }
40555                 if(this.labelmd > 0){
40556                     label.cls += ' col-md-' + this.labelmd;
40557                     container.cls += ' col-md-' + (12 - this.labelmd);
40558                 }
40559                 if(this.labelsm > 0){
40560                     label.cls += ' col-sm-' + this.labelsm;
40561                     container.cls += ' col-sm-' + (12 - this.labelsm);
40562                 }
40563                 if(this.labelxs > 0){
40564                     label.cls += ' col-xs-' + this.labelxs;
40565                     container.cls += ' col-xs-' + (12 - this.labelxs);
40566                 }
40567             }
40568         }
40569
40570         cfg.cn = [
40571             label,
40572             container,
40573             hiddenInput
40574         ];
40575         
40576         var settings = this;
40577
40578         ['xs','sm','md','lg'].map(function(size){
40579             if (settings[size]) {
40580                 cfg.cls += ' col-' + size + '-' + settings[size];
40581             }
40582         });
40583         
40584         return cfg;
40585     },
40586     
40587     initEvents : function()
40588     {
40589         this.indicator = this.indicatorEl();
40590         
40591         this.initCurrencyEvent();
40592         
40593         this.initNumberEvent();
40594     },
40595     
40596     initCurrencyEvent : function()
40597     {
40598         if (!this.store) {
40599             throw "can not find store for combo";
40600         }
40601         
40602         this.store = Roo.factory(this.store, Roo.data);
40603         this.store.parent = this;
40604         
40605         this.createList();
40606         
40607         this.triggerEl = this.el.select('.input-group-addon', true).first();
40608         
40609         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40610         
40611         var _this = this;
40612         
40613         (function(){
40614             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40615             _this.list.setWidth(lw);
40616         }).defer(100);
40617         
40618         this.list.on('mouseover', this.onViewOver, this);
40619         this.list.on('mousemove', this.onViewMove, this);
40620         this.list.on('scroll', this.onViewScroll, this);
40621         
40622         if(!this.tpl){
40623             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40624         }
40625         
40626         this.view = new Roo.View(this.list, this.tpl, {
40627             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40628         });
40629         
40630         this.view.on('click', this.onViewClick, this);
40631         
40632         this.store.on('beforeload', this.onBeforeLoad, this);
40633         this.store.on('load', this.onLoad, this);
40634         this.store.on('loadexception', this.onLoadException, this);
40635         
40636         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40637             "up" : function(e){
40638                 this.inKeyMode = true;
40639                 this.selectPrev();
40640             },
40641
40642             "down" : function(e){
40643                 if(!this.isExpanded()){
40644                     this.onTriggerClick();
40645                 }else{
40646                     this.inKeyMode = true;
40647                     this.selectNext();
40648                 }
40649             },
40650
40651             "enter" : function(e){
40652                 this.collapse();
40653                 
40654                 if(this.fireEvent("specialkey", this, e)){
40655                     this.onViewClick(false);
40656                 }
40657                 
40658                 return true;
40659             },
40660
40661             "esc" : function(e){
40662                 this.collapse();
40663             },
40664
40665             "tab" : function(e){
40666                 this.collapse();
40667                 
40668                 if(this.fireEvent("specialkey", this, e)){
40669                     this.onViewClick(false);
40670                 }
40671                 
40672                 return true;
40673             },
40674
40675             scope : this,
40676
40677             doRelay : function(foo, bar, hname){
40678                 if(hname == 'down' || this.scope.isExpanded()){
40679                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40680                 }
40681                 return true;
40682             },
40683
40684             forceKeyDown: true
40685         });
40686         
40687         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40688         
40689     },
40690     
40691     initNumberEvent : function(e)
40692     {
40693         this.inputEl().on("keydown" , this.fireKey,  this);
40694         this.inputEl().on("focus", this.onFocus,  this);
40695         this.inputEl().on("blur", this.onBlur,  this);
40696         
40697         this.inputEl().relayEvent('keyup', this);
40698         
40699         if(this.indicator){
40700             this.indicator.addClass('invisible');
40701         }
40702  
40703         this.originalValue = this.getValue();
40704         
40705         if(this.validationEvent == 'keyup'){
40706             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40707             this.inputEl().on('keyup', this.filterValidation, this);
40708         }
40709         else if(this.validationEvent !== false){
40710             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40711         }
40712         
40713         if(this.selectOnFocus){
40714             this.on("focus", this.preFocus, this);
40715             
40716         }
40717         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40718             this.inputEl().on("keypress", this.filterKeys, this);
40719         } else {
40720             this.inputEl().relayEvent('keypress', this);
40721         }
40722         
40723         var allowed = "0123456789";
40724         
40725         if(this.allowDecimals){
40726             allowed += this.decimalSeparator;
40727         }
40728         
40729         if(this.allowNegative){
40730             allowed += "-";
40731         }
40732         
40733         if(this.thousandsDelimiter) {
40734             allowed += ",";
40735         }
40736         
40737         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40738         
40739         var keyPress = function(e){
40740             
40741             var k = e.getKey();
40742             
40743             var c = e.getCharCode();
40744             
40745             if(
40746                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40747                     allowed.indexOf(String.fromCharCode(c)) === -1
40748             ){
40749                 e.stopEvent();
40750                 return;
40751             }
40752             
40753             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40754                 return;
40755             }
40756             
40757             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40758                 e.stopEvent();
40759             }
40760         };
40761         
40762         this.inputEl().on("keypress", keyPress, this);
40763         
40764     },
40765     
40766     onTriggerClick : function(e)
40767     {   
40768         if(this.disabled){
40769             return;
40770         }
40771         
40772         this.page = 0;
40773         this.loadNext = false;
40774         
40775         if(this.isExpanded()){
40776             this.collapse();
40777             return;
40778         }
40779         
40780         this.hasFocus = true;
40781         
40782         if(this.triggerAction == 'all') {
40783             this.doQuery(this.allQuery, true);
40784             return;
40785         }
40786         
40787         this.doQuery(this.getRawValue());
40788     },
40789     
40790     getCurrency : function()
40791     {   
40792         var v = this.currencyEl().getValue();
40793         
40794         return v;
40795     },
40796     
40797     restrictHeight : function()
40798     {
40799         this.list.alignTo(this.currencyEl(), this.listAlign);
40800         this.list.alignTo(this.currencyEl(), this.listAlign);
40801     },
40802     
40803     onViewClick : function(view, doFocus, el, e)
40804     {
40805         var index = this.view.getSelectedIndexes()[0];
40806         
40807         var r = this.store.getAt(index);
40808         
40809         if(r){
40810             this.onSelect(r, index);
40811         }
40812     },
40813     
40814     onSelect : function(record, index){
40815         
40816         if(this.fireEvent('beforeselect', this, record, index) !== false){
40817         
40818             this.setFromCurrencyData(index > -1 ? record.data : false);
40819             
40820             this.collapse();
40821             
40822             this.fireEvent('select', this, record, index);
40823         }
40824     },
40825     
40826     setFromCurrencyData : function(o)
40827     {
40828         var currency = '';
40829         
40830         this.lastCurrency = o;
40831         
40832         if (this.currencyField) {
40833             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40834         } else {
40835             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40836         }
40837         
40838         this.lastSelectionText = currency;
40839         
40840         //setting default currency
40841         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40842             this.setCurrency(this.defaultCurrency);
40843             return;
40844         }
40845         
40846         this.setCurrency(currency);
40847     },
40848     
40849     setFromData : function(o)
40850     {
40851         var c = {};
40852         
40853         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40854         
40855         this.setFromCurrencyData(c);
40856         
40857         var value = '';
40858         
40859         if (this.name) {
40860             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40861         } else {
40862             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40863         }
40864         
40865         this.setValue(value);
40866         
40867     },
40868     
40869     setCurrency : function(v)
40870     {   
40871         this.currencyValue = v;
40872         
40873         if(this.rendered){
40874             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40875             this.validate();
40876         }
40877     },
40878     
40879     setValue : function(v)
40880     {
40881         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40882         
40883         this.value = v;
40884         
40885         if(this.rendered){
40886             
40887             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40888             
40889             this.inputEl().dom.value = (v == '') ? '' :
40890                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40891             
40892             if(!this.allowZero && v === '0') {
40893                 this.hiddenEl().dom.value = '';
40894                 this.inputEl().dom.value = '';
40895             }
40896             
40897             this.validate();
40898         }
40899     },
40900     
40901     getRawValue : function()
40902     {
40903         var v = this.inputEl().getValue();
40904         
40905         return v;
40906     },
40907     
40908     getValue : function()
40909     {
40910         return this.fixPrecision(this.parseValue(this.getRawValue()));
40911     },
40912     
40913     parseValue : function(value)
40914     {
40915         if(this.thousandsDelimiter) {
40916             value += "";
40917             r = new RegExp(",", "g");
40918             value = value.replace(r, "");
40919         }
40920         
40921         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40922         return isNaN(value) ? '' : value;
40923         
40924     },
40925     
40926     fixPrecision : function(value)
40927     {
40928         if(this.thousandsDelimiter) {
40929             value += "";
40930             r = new RegExp(",", "g");
40931             value = value.replace(r, "");
40932         }
40933         
40934         var nan = isNaN(value);
40935         
40936         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40937             return nan ? '' : value;
40938         }
40939         return parseFloat(value).toFixed(this.decimalPrecision);
40940     },
40941     
40942     decimalPrecisionFcn : function(v)
40943     {
40944         return Math.floor(v);
40945     },
40946     
40947     validateValue : function(value)
40948     {
40949         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40950             return false;
40951         }
40952         
40953         var num = this.parseValue(value);
40954         
40955         if(isNaN(num)){
40956             this.markInvalid(String.format(this.nanText, value));
40957             return false;
40958         }
40959         
40960         if(num < this.minValue){
40961             this.markInvalid(String.format(this.minText, this.minValue));
40962             return false;
40963         }
40964         
40965         if(num > this.maxValue){
40966             this.markInvalid(String.format(this.maxText, this.maxValue));
40967             return false;
40968         }
40969         
40970         return true;
40971     },
40972     
40973     validate : function()
40974     {
40975         if(this.disabled || this.allowBlank){
40976             this.markValid();
40977             return true;
40978         }
40979         
40980         var currency = this.getCurrency();
40981         
40982         if(this.validateValue(this.getRawValue()) && currency.length){
40983             this.markValid();
40984             return true;
40985         }
40986         
40987         this.markInvalid();
40988         return false;
40989     },
40990     
40991     getName: function()
40992     {
40993         return this.name;
40994     },
40995     
40996     beforeBlur : function()
40997     {
40998         if(!this.castInt){
40999             return;
41000         }
41001         
41002         var v = this.parseValue(this.getRawValue());
41003         
41004         if(v || v == 0){
41005             this.setValue(v);
41006         }
41007     },
41008     
41009     onBlur : function()
41010     {
41011         this.beforeBlur();
41012         
41013         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41014             //this.el.removeClass(this.focusClass);
41015         }
41016         
41017         this.hasFocus = false;
41018         
41019         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41020             this.validate();
41021         }
41022         
41023         var v = this.getValue();
41024         
41025         if(String(v) !== String(this.startValue)){
41026             this.fireEvent('change', this, v, this.startValue);
41027         }
41028         
41029         this.fireEvent("blur", this);
41030     },
41031     
41032     inputEl : function()
41033     {
41034         return this.el.select('.roo-money-amount-input', true).first();
41035     },
41036     
41037     currencyEl : function()
41038     {
41039         return this.el.select('.roo-money-currency-input', true).first();
41040     },
41041     
41042     hiddenEl : function()
41043     {
41044         return this.el.select('input.hidden-number-input',true).first();
41045     }
41046     
41047 });