Merge branch 'master' of http://git.roojs.com/roojs1
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     
921     setWeight : function(str)
922     {
923         this.el.removeClass(this.weightClass);
924         this.el.addClass('btn-' + str);        
925     }
926     
927     
928 });
929
930  /*
931  * - LGPL
932  *
933  * column
934  * 
935  */
936
937 /**
938  * @class Roo.bootstrap.Column
939  * @extends Roo.bootstrap.Component
940  * Bootstrap Column class
941  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
942  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
943  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
944  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
945  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
946  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
947  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
948  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
949  *
950  * 
951  * @cfg {Boolean} hidden (true|false) hide the element
952  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
953  * @cfg {String} fa (ban|check|...) font awesome icon
954  * @cfg {Number} fasize (1|2|....) font awsome size
955
956  * @cfg {String} icon (info-sign|check|...) glyphicon name
957
958  * @cfg {String} html content of column.
959  * 
960  * @constructor
961  * Create a new Column
962  * @param {Object} config The config object
963  */
964
965 Roo.bootstrap.Column = function(config){
966     Roo.bootstrap.Column.superclass.constructor.call(this, config);
967 };
968
969 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
970     
971     xs: false,
972     sm: false,
973     md: false,
974     lg: false,
975     xsoff: false,
976     smoff: false,
977     mdoff: false,
978     lgoff: false,
979     html: '',
980     offset: 0,
981     alert: false,
982     fa: false,
983     icon : false,
984     hidden : false,
985     fasize : 1,
986     
987     getAutoCreate : function(){
988         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
989         
990         cfg = {
991             tag: 'div',
992             cls: 'column'
993         };
994         
995         var settings=this;
996         ['xs','sm','md','lg'].map(function(size){
997             //Roo.log( size + ':' + settings[size]);
998             
999             if (settings[size+'off'] !== false) {
1000                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1001             }
1002             
1003             if (settings[size] === false) {
1004                 return;
1005             }
1006             
1007             if (!settings[size]) { // 0 = hidden
1008                 cfg.cls += ' hidden-' + size;
1009                 return;
1010             }
1011             cfg.cls += ' col-' + size + '-' + settings[size];
1012             
1013         });
1014         
1015         if (this.hidden) {
1016             cfg.cls += ' hidden';
1017         }
1018         
1019         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1020             cfg.cls +=' alert alert-' + this.alert;
1021         }
1022         
1023         
1024         if (this.html.length) {
1025             cfg.html = this.html;
1026         }
1027         if (this.fa) {
1028             var fasize = '';
1029             if (this.fasize > 1) {
1030                 fasize = ' fa-' + this.fasize + 'x';
1031             }
1032             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1033             
1034             
1035         }
1036         if (this.icon) {
1037             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1038         }
1039         
1040         return cfg;
1041     }
1042    
1043 });
1044
1045  
1046
1047  /*
1048  * - LGPL
1049  *
1050  * page container.
1051  * 
1052  */
1053
1054
1055 /**
1056  * @class Roo.bootstrap.Container
1057  * @extends Roo.bootstrap.Component
1058  * Bootstrap Container class
1059  * @cfg {Boolean} jumbotron is it a jumbotron element
1060  * @cfg {String} html content of element
1061  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1062  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1063  * @cfg {String} header content of header (for panel)
1064  * @cfg {String} footer content of footer (for panel)
1065  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1066  * @cfg {String} tag (header|aside|section) type of HTML tag.
1067  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1068  * @cfg {String} fa font awesome icon
1069  * @cfg {String} icon (info-sign|check|...) glyphicon name
1070  * @cfg {Boolean} hidden (true|false) hide the element
1071  * @cfg {Boolean} expandable (true|false) default false
1072  * @cfg {Boolean} expanded (true|false) default true
1073  * @cfg {String} rheader contet on the right of header
1074  * @cfg {Boolean} clickable (true|false) default false
1075
1076  *     
1077  * @constructor
1078  * Create a new Container
1079  * @param {Object} config The config object
1080  */
1081
1082 Roo.bootstrap.Container = function(config){
1083     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1084     
1085     this.addEvents({
1086         // raw events
1087          /**
1088          * @event expand
1089          * After the panel has been expand
1090          * 
1091          * @param {Roo.bootstrap.Container} this
1092          */
1093         "expand" : true,
1094         /**
1095          * @event collapse
1096          * After the panel has been collapsed
1097          * 
1098          * @param {Roo.bootstrap.Container} this
1099          */
1100         "collapse" : true,
1101         /**
1102          * @event click
1103          * When a element is chick
1104          * @param {Roo.bootstrap.Container} this
1105          * @param {Roo.EventObject} e
1106          */
1107         "click" : true
1108     });
1109 };
1110
1111 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1112     
1113     jumbotron : false,
1114     well: '',
1115     panel : '',
1116     header: '',
1117     footer : '',
1118     sticky: '',
1119     tag : false,
1120     alert : false,
1121     fa: false,
1122     icon : false,
1123     expandable : false,
1124     rheader : '',
1125     expanded : true,
1126     clickable: false,
1127   
1128      
1129     getChildContainer : function() {
1130         
1131         if(!this.el){
1132             return false;
1133         }
1134         
1135         if (this.panel.length) {
1136             return this.el.select('.panel-body',true).first();
1137         }
1138         
1139         return this.el;
1140     },
1141     
1142     
1143     getAutoCreate : function(){
1144         
1145         var cfg = {
1146             tag : this.tag || 'div',
1147             html : '',
1148             cls : ''
1149         };
1150         if (this.jumbotron) {
1151             cfg.cls = 'jumbotron';
1152         }
1153         
1154         
1155         
1156         // - this is applied by the parent..
1157         //if (this.cls) {
1158         //    cfg.cls = this.cls + '';
1159         //}
1160         
1161         if (this.sticky.length) {
1162             
1163             var bd = Roo.get(document.body);
1164             if (!bd.hasClass('bootstrap-sticky')) {
1165                 bd.addClass('bootstrap-sticky');
1166                 Roo.select('html',true).setStyle('height', '100%');
1167             }
1168              
1169             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1170         }
1171         
1172         
1173         if (this.well.length) {
1174             switch (this.well) {
1175                 case 'lg':
1176                 case 'sm':
1177                     cfg.cls +=' well well-' +this.well;
1178                     break;
1179                 default:
1180                     cfg.cls +=' well';
1181                     break;
1182             }
1183         }
1184         
1185         if (this.hidden) {
1186             cfg.cls += ' hidden';
1187         }
1188         
1189         
1190         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1191             cfg.cls +=' alert alert-' + this.alert;
1192         }
1193         
1194         var body = cfg;
1195         
1196         if (this.panel.length) {
1197             cfg.cls += ' panel panel-' + this.panel;
1198             cfg.cn = [];
1199             if (this.header.length) {
1200                 
1201                 var h = [];
1202                 
1203                 if(this.expandable){
1204                     
1205                     cfg.cls = cfg.cls + ' expandable';
1206                     
1207                     h.push({
1208                         tag: 'i',
1209                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1210                     });
1211                     
1212                 }
1213                 
1214                 h.push(
1215                     {
1216                         tag: 'span',
1217                         cls : 'panel-title',
1218                         html : (this.expandable ? '&nbsp;' : '') + this.header
1219                     },
1220                     {
1221                         tag: 'span',
1222                         cls: 'panel-header-right',
1223                         html: this.rheader
1224                     }
1225                 );
1226                 
1227                 cfg.cn.push({
1228                     cls : 'panel-heading',
1229                     style : this.expandable ? 'cursor: pointer' : '',
1230                     cn : h
1231                 });
1232                 
1233             }
1234             
1235             body = false;
1236             cfg.cn.push({
1237                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1238                 html : this.html
1239             });
1240             
1241             
1242             if (this.footer.length) {
1243                 cfg.cn.push({
1244                     cls : 'panel-footer',
1245                     html : this.footer
1246                     
1247                 });
1248             }
1249             
1250         }
1251         
1252         if (body) {
1253             body.html = this.html || cfg.html;
1254             // prefix with the icons..
1255             if (this.fa) {
1256                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1257             }
1258             if (this.icon) {
1259                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1260             }
1261             
1262             
1263         }
1264         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1265             cfg.cls =  'container';
1266         }
1267         
1268         return cfg;
1269     },
1270     
1271     initEvents: function() 
1272     {
1273         if(this.expandable){
1274             var headerEl = this.headerEl();
1275         
1276             if(headerEl){
1277                 headerEl.on('click', this.onToggleClick, this);
1278             }
1279         }
1280         
1281         if(this.clickable){
1282             this.el.on('click', this.onClick, this);
1283         }
1284         
1285     },
1286     
1287     onToggleClick : function()
1288     {
1289         var headerEl = this.headerEl();
1290         
1291         if(!headerEl){
1292             return;
1293         }
1294         
1295         if(this.expanded){
1296             this.collapse();
1297             return;
1298         }
1299         
1300         this.expand();
1301     },
1302     
1303     expand : function()
1304     {
1305         if(this.fireEvent('expand', this)) {
1306             
1307             this.expanded = true;
1308             
1309             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1310             
1311             this.el.select('.panel-body',true).first().removeClass('hide');
1312             
1313             var toggleEl = this.toggleEl();
1314
1315             if(!toggleEl){
1316                 return;
1317             }
1318
1319             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1320         }
1321         
1322     },
1323     
1324     collapse : function()
1325     {
1326         if(this.fireEvent('collapse', this)) {
1327             
1328             this.expanded = false;
1329             
1330             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1331             this.el.select('.panel-body',true).first().addClass('hide');
1332         
1333             var toggleEl = this.toggleEl();
1334
1335             if(!toggleEl){
1336                 return;
1337             }
1338
1339             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1340         }
1341     },
1342     
1343     toggleEl : function()
1344     {
1345         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1346             return;
1347         }
1348         
1349         return this.el.select('.panel-heading .fa',true).first();
1350     },
1351     
1352     headerEl : function()
1353     {
1354         if(!this.el || !this.panel.length || !this.header.length){
1355             return;
1356         }
1357         
1358         return this.el.select('.panel-heading',true).first()
1359     },
1360     
1361     bodyEl : function()
1362     {
1363         if(!this.el || !this.panel.length){
1364             return;
1365         }
1366         
1367         return this.el.select('.panel-body',true).first()
1368     },
1369     
1370     titleEl : function()
1371     {
1372         if(!this.el || !this.panel.length || !this.header.length){
1373             return;
1374         }
1375         
1376         return this.el.select('.panel-title',true).first();
1377     },
1378     
1379     setTitle : function(v)
1380     {
1381         var titleEl = this.titleEl();
1382         
1383         if(!titleEl){
1384             return;
1385         }
1386         
1387         titleEl.dom.innerHTML = v;
1388     },
1389     
1390     getTitle : function()
1391     {
1392         
1393         var titleEl = this.titleEl();
1394         
1395         if(!titleEl){
1396             return '';
1397         }
1398         
1399         return titleEl.dom.innerHTML;
1400     },
1401     
1402     setRightTitle : function(v)
1403     {
1404         var t = this.el.select('.panel-header-right',true).first();
1405         
1406         if(!t){
1407             return;
1408         }
1409         
1410         t.dom.innerHTML = v;
1411     },
1412     
1413     onClick : function(e)
1414     {
1415         e.preventDefault();
1416         
1417         this.fireEvent('click', this, e);
1418     }
1419 });
1420
1421  /*
1422  * - LGPL
1423  *
1424  * image
1425  * 
1426  */
1427
1428
1429 /**
1430  * @class Roo.bootstrap.Img
1431  * @extends Roo.bootstrap.Component
1432  * Bootstrap Img class
1433  * @cfg {Boolean} imgResponsive false | true
1434  * @cfg {String} border rounded | circle | thumbnail
1435  * @cfg {String} src image source
1436  * @cfg {String} alt image alternative text
1437  * @cfg {String} href a tag href
1438  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1439  * @cfg {String} xsUrl xs image source
1440  * @cfg {String} smUrl sm image source
1441  * @cfg {String} mdUrl md image source
1442  * @cfg {String} lgUrl lg image source
1443  * 
1444  * @constructor
1445  * Create a new Input
1446  * @param {Object} config The config object
1447  */
1448
1449 Roo.bootstrap.Img = function(config){
1450     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1451     
1452     this.addEvents({
1453         // img events
1454         /**
1455          * @event click
1456          * The img click event for the img.
1457          * @param {Roo.EventObject} e
1458          */
1459         "click" : true
1460     });
1461 };
1462
1463 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1464     
1465     imgResponsive: true,
1466     border: '',
1467     src: 'about:blank',
1468     href: false,
1469     target: false,
1470     xsUrl: '',
1471     smUrl: '',
1472     mdUrl: '',
1473     lgUrl: '',
1474
1475     getAutoCreate : function()
1476     {   
1477         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1478             return this.createSingleImg();
1479         }
1480         
1481         var cfg = {
1482             tag: 'div',
1483             cls: 'roo-image-responsive-group',
1484             cn: []
1485         };
1486         var _this = this;
1487         
1488         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1489             
1490             if(!_this[size + 'Url']){
1491                 return;
1492             }
1493             
1494             var img = {
1495                 tag: 'img',
1496                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1497                 html: _this.html || cfg.html,
1498                 src: _this[size + 'Url']
1499             };
1500             
1501             img.cls += ' roo-image-responsive-' + size;
1502             
1503             var s = ['xs', 'sm', 'md', 'lg'];
1504             
1505             s.splice(s.indexOf(size), 1);
1506             
1507             Roo.each(s, function(ss){
1508                 img.cls += ' hidden-' + ss;
1509             });
1510             
1511             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1512                 cfg.cls += ' img-' + _this.border;
1513             }
1514             
1515             if(_this.alt){
1516                 cfg.alt = _this.alt;
1517             }
1518             
1519             if(_this.href){
1520                 var a = {
1521                     tag: 'a',
1522                     href: _this.href,
1523                     cn: [
1524                         img
1525                     ]
1526                 };
1527
1528                 if(this.target){
1529                     a.target = _this.target;
1530                 }
1531             }
1532             
1533             cfg.cn.push((_this.href) ? a : img);
1534             
1535         });
1536         
1537         return cfg;
1538     },
1539     
1540     createSingleImg : function()
1541     {
1542         var cfg = {
1543             tag: 'img',
1544             cls: (this.imgResponsive) ? 'img-responsive' : '',
1545             html : null,
1546             src : 'about:blank'  // just incase src get's set to undefined?!?
1547         };
1548         
1549         cfg.html = this.html || cfg.html;
1550         
1551         cfg.src = this.src || cfg.src;
1552         
1553         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1554             cfg.cls += ' img-' + this.border;
1555         }
1556         
1557         if(this.alt){
1558             cfg.alt = this.alt;
1559         }
1560         
1561         if(this.href){
1562             var a = {
1563                 tag: 'a',
1564                 href: this.href,
1565                 cn: [
1566                     cfg
1567                 ]
1568             };
1569             
1570             if(this.target){
1571                 a.target = this.target;
1572             }
1573             
1574         }
1575         
1576         return (this.href) ? a : cfg;
1577     },
1578     
1579     initEvents: function() 
1580     {
1581         if(!this.href){
1582             this.el.on('click', this.onClick, this);
1583         }
1584         
1585     },
1586     
1587     onClick : function(e)
1588     {
1589         Roo.log('img onclick');
1590         this.fireEvent('click', this, e);
1591     },
1592     /**
1593      * Sets the url of the image - used to update it
1594      * @param {String} url the url of the image
1595      */
1596     
1597     setSrc : function(url)
1598     {
1599         this.src =  url;
1600         
1601         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1602             this.el.dom.src =  url;
1603             return;
1604         }
1605         
1606         this.el.select('img', true).first().dom.src =  url;
1607     }
1608     
1609     
1610    
1611 });
1612
1613  /*
1614  * - LGPL
1615  *
1616  * image
1617  * 
1618  */
1619
1620
1621 /**
1622  * @class Roo.bootstrap.Link
1623  * @extends Roo.bootstrap.Component
1624  * Bootstrap Link Class
1625  * @cfg {String} alt image alternative text
1626  * @cfg {String} href a tag href
1627  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1628  * @cfg {String} html the content of the link.
1629  * @cfg {String} anchor name for the anchor link
1630  * @cfg {String} fa - favicon
1631
1632  * @cfg {Boolean} preventDefault (true | false) default false
1633
1634  * 
1635  * @constructor
1636  * Create a new Input
1637  * @param {Object} config The config object
1638  */
1639
1640 Roo.bootstrap.Link = function(config){
1641     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1642     
1643     this.addEvents({
1644         // img events
1645         /**
1646          * @event click
1647          * The img click event for the img.
1648          * @param {Roo.EventObject} e
1649          */
1650         "click" : true
1651     });
1652 };
1653
1654 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1655     
1656     href: false,
1657     target: false,
1658     preventDefault: false,
1659     anchor : false,
1660     alt : false,
1661     fa: false,
1662
1663
1664     getAutoCreate : function()
1665     {
1666         var html = this.html || '';
1667         
1668         if (this.fa !== false) {
1669             html = '<i class="fa fa-' + this.fa + '"></i>';
1670         }
1671         var cfg = {
1672             tag: 'a'
1673         };
1674         // anchor's do not require html/href...
1675         if (this.anchor === false) {
1676             cfg.html = html;
1677             cfg.href = this.href || '#';
1678         } else {
1679             cfg.name = this.anchor;
1680             if (this.html !== false || this.fa !== false) {
1681                 cfg.html = html;
1682             }
1683             if (this.href !== false) {
1684                 cfg.href = this.href;
1685             }
1686         }
1687         
1688         if(this.alt !== false){
1689             cfg.alt = this.alt;
1690         }
1691         
1692         
1693         if(this.target !== false) {
1694             cfg.target = this.target;
1695         }
1696         
1697         return cfg;
1698     },
1699     
1700     initEvents: function() {
1701         
1702         if(!this.href || this.preventDefault){
1703             this.el.on('click', this.onClick, this);
1704         }
1705     },
1706     
1707     onClick : function(e)
1708     {
1709         if(this.preventDefault){
1710             e.preventDefault();
1711         }
1712         //Roo.log('img onclick');
1713         this.fireEvent('click', this, e);
1714     }
1715    
1716 });
1717
1718  /*
1719  * - LGPL
1720  *
1721  * header
1722  * 
1723  */
1724
1725 /**
1726  * @class Roo.bootstrap.Header
1727  * @extends Roo.bootstrap.Component
1728  * Bootstrap Header class
1729  * @cfg {String} html content of header
1730  * @cfg {Number} level (1|2|3|4|5|6) default 1
1731  * 
1732  * @constructor
1733  * Create a new Header
1734  * @param {Object} config The config object
1735  */
1736
1737
1738 Roo.bootstrap.Header  = function(config){
1739     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1740 };
1741
1742 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1743     
1744     //href : false,
1745     html : false,
1746     level : 1,
1747     
1748     
1749     
1750     getAutoCreate : function(){
1751         
1752         
1753         
1754         var cfg = {
1755             tag: 'h' + (1 *this.level),
1756             html: this.html || ''
1757         } ;
1758         
1759         return cfg;
1760     }
1761    
1762 });
1763
1764  
1765
1766  /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776  
1777 /**
1778  * @class Roo.bootstrap.MenuMgr
1779  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1780  * @singleton
1781  */
1782 Roo.bootstrap.MenuMgr = function(){
1783    var menus, active, groups = {}, attached = false, lastShow = new Date();
1784
1785    // private - called when first menu is created
1786    function init(){
1787        menus = {};
1788        active = new Roo.util.MixedCollection();
1789        Roo.get(document).addKeyListener(27, function(){
1790            if(active.length > 0){
1791                hideAll();
1792            }
1793        });
1794    }
1795
1796    // private
1797    function hideAll(){
1798        if(active && active.length > 0){
1799            var c = active.clone();
1800            c.each(function(m){
1801                m.hide();
1802            });
1803        }
1804    }
1805
1806    // private
1807    function onHide(m){
1808        active.remove(m);
1809        if(active.length < 1){
1810            Roo.get(document).un("mouseup", onMouseDown);
1811             
1812            attached = false;
1813        }
1814    }
1815
1816    // private
1817    function onShow(m){
1818        var last = active.last();
1819        lastShow = new Date();
1820        active.add(m);
1821        if(!attached){
1822           Roo.get(document).on("mouseup", onMouseDown);
1823            
1824            attached = true;
1825        }
1826        if(m.parentMenu){
1827           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1828           m.parentMenu.activeChild = m;
1829        }else if(last && last.isVisible()){
1830           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1831        }
1832    }
1833
1834    // private
1835    function onBeforeHide(m){
1836        if(m.activeChild){
1837            m.activeChild.hide();
1838        }
1839        if(m.autoHideTimer){
1840            clearTimeout(m.autoHideTimer);
1841            delete m.autoHideTimer;
1842        }
1843    }
1844
1845    // private
1846    function onBeforeShow(m){
1847        var pm = m.parentMenu;
1848        if(!pm && !m.allowOtherMenus){
1849            hideAll();
1850        }else if(pm && pm.activeChild && active != m){
1851            pm.activeChild.hide();
1852        }
1853    }
1854
1855    // private this should really trigger on mouseup..
1856    function onMouseDown(e){
1857         Roo.log("on Mouse Up");
1858         
1859         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1860             Roo.log("MenuManager hideAll");
1861             hideAll();
1862             e.stopEvent();
1863         }
1864         
1865         
1866    }
1867
1868    // private
1869    function onBeforeCheck(mi, state){
1870        if(state){
1871            var g = groups[mi.group];
1872            for(var i = 0, l = g.length; i < l; i++){
1873                if(g[i] != mi){
1874                    g[i].setChecked(false);
1875                }
1876            }
1877        }
1878    }
1879
1880    return {
1881
1882        /**
1883         * Hides all menus that are currently visible
1884         */
1885        hideAll : function(){
1886             hideAll();  
1887        },
1888
1889        // private
1890        register : function(menu){
1891            if(!menus){
1892                init();
1893            }
1894            menus[menu.id] = menu;
1895            menu.on("beforehide", onBeforeHide);
1896            menu.on("hide", onHide);
1897            menu.on("beforeshow", onBeforeShow);
1898            menu.on("show", onShow);
1899            var g = menu.group;
1900            if(g && menu.events["checkchange"]){
1901                if(!groups[g]){
1902                    groups[g] = [];
1903                }
1904                groups[g].push(menu);
1905                menu.on("checkchange", onCheck);
1906            }
1907        },
1908
1909         /**
1910          * Returns a {@link Roo.menu.Menu} object
1911          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1912          * be used to generate and return a new Menu instance.
1913          */
1914        get : function(menu){
1915            if(typeof menu == "string"){ // menu id
1916                return menus[menu];
1917            }else if(menu.events){  // menu instance
1918                return menu;
1919            }
1920            /*else if(typeof menu.length == 'number'){ // array of menu items?
1921                return new Roo.bootstrap.Menu({items:menu});
1922            }else{ // otherwise, must be a config
1923                return new Roo.bootstrap.Menu(menu);
1924            }
1925            */
1926            return false;
1927        },
1928
1929        // private
1930        unregister : function(menu){
1931            delete menus[menu.id];
1932            menu.un("beforehide", onBeforeHide);
1933            menu.un("hide", onHide);
1934            menu.un("beforeshow", onBeforeShow);
1935            menu.un("show", onShow);
1936            var g = menu.group;
1937            if(g && menu.events["checkchange"]){
1938                groups[g].remove(menu);
1939                menu.un("checkchange", onCheck);
1940            }
1941        },
1942
1943        // private
1944        registerCheckable : function(menuItem){
1945            var g = menuItem.group;
1946            if(g){
1947                if(!groups[g]){
1948                    groups[g] = [];
1949                }
1950                groups[g].push(menuItem);
1951                menuItem.on("beforecheckchange", onBeforeCheck);
1952            }
1953        },
1954
1955        // private
1956        unregisterCheckable : function(menuItem){
1957            var g = menuItem.group;
1958            if(g){
1959                groups[g].remove(menuItem);
1960                menuItem.un("beforecheckchange", onBeforeCheck);
1961            }
1962        }
1963    };
1964 }();/*
1965  * - LGPL
1966  *
1967  * menu
1968  * 
1969  */
1970
1971 /**
1972  * @class Roo.bootstrap.Menu
1973  * @extends Roo.bootstrap.Component
1974  * Bootstrap Menu class - container for MenuItems
1975  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1976  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1977  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1978  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1979  * 
1980  * @constructor
1981  * Create a new Menu
1982  * @param {Object} config The config object
1983  */
1984
1985
1986 Roo.bootstrap.Menu = function(config){
1987     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1988     if (this.registerMenu && this.type != 'treeview')  {
1989         Roo.bootstrap.MenuMgr.register(this);
1990     }
1991     this.addEvents({
1992         /**
1993          * @event beforeshow
1994          * Fires before this menu is displayed
1995          * @param {Roo.menu.Menu} this
1996          */
1997         beforeshow : true,
1998         /**
1999          * @event beforehide
2000          * Fires before this menu is hidden
2001          * @param {Roo.menu.Menu} this
2002          */
2003         beforehide : true,
2004         /**
2005          * @event show
2006          * Fires after this menu is displayed
2007          * @param {Roo.menu.Menu} this
2008          */
2009         show : true,
2010         /**
2011          * @event hide
2012          * Fires after this menu is hidden
2013          * @param {Roo.menu.Menu} this
2014          */
2015         hide : true,
2016         /**
2017          * @event click
2018          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2019          * @param {Roo.menu.Menu} this
2020          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2021          * @param {Roo.EventObject} e
2022          */
2023         click : true,
2024         /**
2025          * @event mouseover
2026          * Fires when the mouse is hovering over this menu
2027          * @param {Roo.menu.Menu} this
2028          * @param {Roo.EventObject} e
2029          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2030          */
2031         mouseover : true,
2032         /**
2033          * @event mouseout
2034          * Fires when the mouse exits this menu
2035          * @param {Roo.menu.Menu} this
2036          * @param {Roo.EventObject} e
2037          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038          */
2039         mouseout : true,
2040         /**
2041          * @event itemclick
2042          * Fires when a menu item contained in this menu is clicked
2043          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2044          * @param {Roo.EventObject} e
2045          */
2046         itemclick: true
2047     });
2048     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2049 };
2050
2051 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2052     
2053    /// html : false,
2054     //align : '',
2055     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2056     type: false,
2057     /**
2058      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2059      */
2060     registerMenu : true,
2061     
2062     menuItems :false, // stores the menu items..
2063     
2064     hidden:true,
2065         
2066     parentMenu : false,
2067     
2068     stopEvent : true,
2069     
2070     isLink : false,
2071     
2072     getChildContainer : function() {
2073         return this.el;  
2074     },
2075     
2076     getAutoCreate : function(){
2077          
2078         //if (['right'].indexOf(this.align)!==-1) {
2079         //    cfg.cn[1].cls += ' pull-right'
2080         //}
2081         
2082         
2083         var cfg = {
2084             tag : 'ul',
2085             cls : 'dropdown-menu' ,
2086             style : 'z-index:1000'
2087             
2088         };
2089         
2090         if (this.type === 'submenu') {
2091             cfg.cls = 'submenu active';
2092         }
2093         if (this.type === 'treeview') {
2094             cfg.cls = 'treeview-menu';
2095         }
2096         
2097         return cfg;
2098     },
2099     initEvents : function() {
2100         
2101        // Roo.log("ADD event");
2102        // Roo.log(this.triggerEl.dom);
2103         
2104         this.triggerEl.on('click', this.onTriggerClick, this);
2105         
2106         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2107         
2108         this.triggerEl.addClass('dropdown-toggle');
2109         
2110         if (Roo.isTouch) {
2111             this.el.on('touchstart'  , this.onTouch, this);
2112         }
2113         this.el.on('click' , this.onClick, this);
2114
2115         this.el.on("mouseover", this.onMouseOver, this);
2116         this.el.on("mouseout", this.onMouseOut, this);
2117         
2118     },
2119     
2120     findTargetItem : function(e)
2121     {
2122         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2123         if(!t){
2124             return false;
2125         }
2126         //Roo.log(t);         Roo.log(t.id);
2127         if(t && t.id){
2128             //Roo.log(this.menuitems);
2129             return this.menuitems.get(t.id);
2130             
2131             //return this.items.get(t.menuItemId);
2132         }
2133         
2134         return false;
2135     },
2136     
2137     onTouch : function(e) 
2138     {
2139         Roo.log("menu.onTouch");
2140         //e.stopEvent(); this make the user popdown broken
2141         this.onClick(e);
2142     },
2143     
2144     onClick : function(e)
2145     {
2146         Roo.log("menu.onClick");
2147         
2148         var t = this.findTargetItem(e);
2149         if(!t || t.isContainer){
2150             return;
2151         }
2152         Roo.log(e);
2153         /*
2154         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2155             if(t == this.activeItem && t.shouldDeactivate(e)){
2156                 this.activeItem.deactivate();
2157                 delete this.activeItem;
2158                 return;
2159             }
2160             if(t.canActivate){
2161                 this.setActiveItem(t, true);
2162             }
2163             return;
2164             
2165             
2166         }
2167         */
2168        
2169         Roo.log('pass click event');
2170         
2171         t.onClick(e);
2172         
2173         this.fireEvent("click", this, t, e);
2174         
2175         var _this = this;
2176         
2177         if(!t.href.length || t.href == '#'){
2178             (function() { _this.hide(); }).defer(100);
2179         }
2180         
2181     },
2182     
2183     onMouseOver : function(e){
2184         var t  = this.findTargetItem(e);
2185         //Roo.log(t);
2186         //if(t){
2187         //    if(t.canActivate && !t.disabled){
2188         //        this.setActiveItem(t, true);
2189         //    }
2190         //}
2191         
2192         this.fireEvent("mouseover", this, e, t);
2193     },
2194     isVisible : function(){
2195         return !this.hidden;
2196     },
2197      onMouseOut : function(e){
2198         var t  = this.findTargetItem(e);
2199         
2200         //if(t ){
2201         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2202         //        this.activeItem.deactivate();
2203         //        delete this.activeItem;
2204         //    }
2205         //}
2206         this.fireEvent("mouseout", this, e, t);
2207     },
2208     
2209     
2210     /**
2211      * Displays this menu relative to another element
2212      * @param {String/HTMLElement/Roo.Element} element The element to align to
2213      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2214      * the element (defaults to this.defaultAlign)
2215      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2216      */
2217     show : function(el, pos, parentMenu){
2218         this.parentMenu = parentMenu;
2219         if(!this.el){
2220             this.render();
2221         }
2222         this.fireEvent("beforeshow", this);
2223         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2224     },
2225      /**
2226      * Displays this menu at a specific xy position
2227      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2228      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2229      */
2230     showAt : function(xy, parentMenu, /* private: */_e){
2231         this.parentMenu = parentMenu;
2232         if(!this.el){
2233             this.render();
2234         }
2235         if(_e !== false){
2236             this.fireEvent("beforeshow", this);
2237             //xy = this.el.adjustForConstraints(xy);
2238         }
2239         
2240         //this.el.show();
2241         this.hideMenuItems();
2242         this.hidden = false;
2243         this.triggerEl.addClass('open');
2244         
2245         // reassign x when hitting right
2246         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2247             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2248         }
2249         
2250         // reassign y when hitting bottom
2251         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2252             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2253         }
2254         
2255         // but the list may align on trigger left or trigger top... should it be a properity?
2256         
2257         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2258             this.el.setXY(xy);
2259         }
2260         
2261         this.focus();
2262         this.fireEvent("show", this);
2263     },
2264     
2265     focus : function(){
2266         return;
2267         if(!this.hidden){
2268             this.doFocus.defer(50, this);
2269         }
2270     },
2271
2272     doFocus : function(){
2273         if(!this.hidden){
2274             this.focusEl.focus();
2275         }
2276     },
2277
2278     /**
2279      * Hides this menu and optionally all parent menus
2280      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2281      */
2282     hide : function(deep)
2283     {
2284         
2285         this.hideMenuItems();
2286         if(this.el && this.isVisible()){
2287             this.fireEvent("beforehide", this);
2288             if(this.activeItem){
2289                 this.activeItem.deactivate();
2290                 this.activeItem = null;
2291             }
2292             this.triggerEl.removeClass('open');;
2293             this.hidden = true;
2294             this.fireEvent("hide", this);
2295         }
2296         if(deep === true && this.parentMenu){
2297             this.parentMenu.hide(true);
2298         }
2299     },
2300     
2301     onTriggerClick : function(e)
2302     {
2303         Roo.log('trigger click');
2304         
2305         var target = e.getTarget();
2306         
2307         Roo.log(target.nodeName.toLowerCase());
2308         
2309         if(target.nodeName.toLowerCase() === 'i'){
2310             e.preventDefault();
2311         }
2312         
2313     },
2314     
2315     onTriggerPress  : function(e)
2316     {
2317         Roo.log('trigger press');
2318         //Roo.log(e.getTarget());
2319        // Roo.log(this.triggerEl.dom);
2320        
2321         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2322         var pel = Roo.get(e.getTarget());
2323         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2324             Roo.log('is treeview or dropdown?');
2325             return;
2326         }
2327         
2328         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2329             return;
2330         }
2331         
2332         if (this.isVisible()) {
2333             Roo.log('hide');
2334             this.hide();
2335         } else {
2336             Roo.log('show');
2337             this.show(this.triggerEl, false, false);
2338         }
2339         
2340         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2341             e.stopEvent();
2342         }
2343         
2344     },
2345        
2346     
2347     hideMenuItems : function()
2348     {
2349         Roo.log("hide Menu Items");
2350         if (!this.el) { 
2351             return;
2352         }
2353         //$(backdrop).remove()
2354         this.el.select('.open',true).each(function(aa) {
2355             
2356             aa.removeClass('open');
2357           //var parent = getParent($(this))
2358           //var relatedTarget = { relatedTarget: this }
2359           
2360            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2361           //if (e.isDefaultPrevented()) return
2362            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2363         });
2364     },
2365     addxtypeChild : function (tree, cntr) {
2366         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2367           
2368         this.menuitems.add(comp);
2369         return comp;
2370
2371     },
2372     getEl : function()
2373     {
2374         Roo.log(this.el);
2375         return this.el;
2376     },
2377     
2378     clear : function()
2379     {
2380         this.getEl().dom.innerHTML = '';
2381         this.menuitems.clear();
2382     }
2383 });
2384
2385  
2386  /*
2387  * - LGPL
2388  *
2389  * menu item
2390  * 
2391  */
2392
2393
2394 /**
2395  * @class Roo.bootstrap.MenuItem
2396  * @extends Roo.bootstrap.Component
2397  * Bootstrap MenuItem class
2398  * @cfg {String} html the menu label
2399  * @cfg {String} href the link
2400  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2401  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2402  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2403  * @cfg {String} fa favicon to show on left of menu item.
2404  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2405  * 
2406  * 
2407  * @constructor
2408  * Create a new MenuItem
2409  * @param {Object} config The config object
2410  */
2411
2412
2413 Roo.bootstrap.MenuItem = function(config){
2414     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2415     this.addEvents({
2416         // raw events
2417         /**
2418          * @event click
2419          * The raw click event for the entire grid.
2420          * @param {Roo.bootstrap.MenuItem} this
2421          * @param {Roo.EventObject} e
2422          */
2423         "click" : true
2424     });
2425 };
2426
2427 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2428     
2429     href : false,
2430     html : false,
2431     preventDefault: false,
2432     isContainer : false,
2433     active : false,
2434     fa: false,
2435     
2436     getAutoCreate : function(){
2437         
2438         if(this.isContainer){
2439             return {
2440                 tag: 'li',
2441                 cls: 'dropdown-menu-item'
2442             };
2443         }
2444         var ctag = {
2445             tag: 'span',
2446             html: 'Link'
2447         };
2448         
2449         var anc = {
2450             tag : 'a',
2451             href : '#',
2452             cn : [  ]
2453         };
2454         
2455         if (this.fa !== false) {
2456             anc.cn.push({
2457                 tag : 'i',
2458                 cls : 'fa fa-' + this.fa
2459             });
2460         }
2461         
2462         anc.cn.push(ctag);
2463         
2464         
2465         var cfg= {
2466             tag: 'li',
2467             cls: 'dropdown-menu-item',
2468             cn: [ anc ]
2469         };
2470         if (this.parent().type == 'treeview') {
2471             cfg.cls = 'treeview-menu';
2472         }
2473         if (this.active) {
2474             cfg.cls += ' active';
2475         }
2476         
2477         
2478         
2479         anc.href = this.href || cfg.cn[0].href ;
2480         ctag.html = this.html || cfg.cn[0].html ;
2481         return cfg;
2482     },
2483     
2484     initEvents: function()
2485     {
2486         if (this.parent().type == 'treeview') {
2487             this.el.select('a').on('click', this.onClick, this);
2488         }
2489         
2490         if (this.menu) {
2491             this.menu.parentType = this.xtype;
2492             this.menu.triggerEl = this.el;
2493             this.menu = this.addxtype(Roo.apply({}, this.menu));
2494         }
2495         
2496     },
2497     onClick : function(e)
2498     {
2499         Roo.log('item on click ');
2500         
2501         if(this.preventDefault){
2502             e.preventDefault();
2503         }
2504         //this.parent().hideMenuItems();
2505         
2506         this.fireEvent('click', this, e);
2507     },
2508     getEl : function()
2509     {
2510         return this.el;
2511     } 
2512 });
2513
2514  
2515
2516  /*
2517  * - LGPL
2518  *
2519  * menu separator
2520  * 
2521  */
2522
2523
2524 /**
2525  * @class Roo.bootstrap.MenuSeparator
2526  * @extends Roo.bootstrap.Component
2527  * Bootstrap MenuSeparator class
2528  * 
2529  * @constructor
2530  * Create a new MenuItem
2531  * @param {Object} config The config object
2532  */
2533
2534
2535 Roo.bootstrap.MenuSeparator = function(config){
2536     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2537 };
2538
2539 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2540     
2541     getAutoCreate : function(){
2542         var cfg = {
2543             cls: 'divider',
2544             tag : 'li'
2545         };
2546         
2547         return cfg;
2548     }
2549    
2550 });
2551
2552  
2553
2554  
2555 /*
2556 * Licence: LGPL
2557 */
2558
2559 /**
2560  * @class Roo.bootstrap.Modal
2561  * @extends Roo.bootstrap.Component
2562  * Bootstrap Modal class
2563  * @cfg {String} title Title of dialog
2564  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2565  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2566  * @cfg {Boolean} specificTitle default false
2567  * @cfg {Array} buttons Array of buttons or standard button set..
2568  * @cfg {String} buttonPosition (left|right|center) default right
2569  * @cfg {Boolean} animate default true
2570  * @cfg {Boolean} allow_close default true
2571  * @cfg {Boolean} fitwindow default false
2572  * @cfg {String} size (sm|lg) default empty
2573  * @cfg {Number} max_width set the max width of modal
2574  *
2575  *
2576  * @constructor
2577  * Create a new Modal Dialog
2578  * @param {Object} config The config object
2579  */
2580
2581 Roo.bootstrap.Modal = function(config){
2582     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2583     this.addEvents({
2584         // raw events
2585         /**
2586          * @event btnclick
2587          * The raw btnclick event for the button
2588          * @param {Roo.EventObject} e
2589          */
2590         "btnclick" : true,
2591         /**
2592          * @event resize
2593          * Fire when dialog resize
2594          * @param {Roo.bootstrap.Modal} this
2595          * @param {Roo.EventObject} e
2596          */
2597         "resize" : true
2598     });
2599     this.buttons = this.buttons || [];
2600
2601     if (this.tmpl) {
2602         this.tmpl = Roo.factory(this.tmpl);
2603     }
2604
2605 };
2606
2607 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2608
2609     title : 'test dialog',
2610
2611     buttons : false,
2612
2613     // set on load...
2614
2615     html: false,
2616
2617     tmp: false,
2618
2619     specificTitle: false,
2620
2621     buttonPosition: 'right',
2622
2623     allow_close : true,
2624
2625     animate : true,
2626
2627     fitwindow: false,
2628     
2629      // private
2630     dialogEl: false,
2631     bodyEl:  false,
2632     footerEl:  false,
2633     titleEl:  false,
2634     closeEl:  false,
2635
2636     size: '',
2637     
2638     max_width: 0,
2639     
2640     max_height: 0,
2641     
2642     fit_content: false,
2643
2644     onRender : function(ct, position)
2645     {
2646         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2647
2648         if(!this.el){
2649             var cfg = Roo.apply({},  this.getAutoCreate());
2650             cfg.id = Roo.id();
2651             //if(!cfg.name){
2652             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2653             //}
2654             //if (!cfg.name.length) {
2655             //    delete cfg.name;
2656            // }
2657             if (this.cls) {
2658                 cfg.cls += ' ' + this.cls;
2659             }
2660             if (this.style) {
2661                 cfg.style = this.style;
2662             }
2663             this.el = Roo.get(document.body).createChild(cfg, position);
2664         }
2665         //var type = this.el.dom.type;
2666
2667
2668         if(this.tabIndex !== undefined){
2669             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2670         }
2671
2672         this.dialogEl = this.el.select('.modal-dialog',true).first();
2673         this.bodyEl = this.el.select('.modal-body',true).first();
2674         this.closeEl = this.el.select('.modal-header .close', true).first();
2675         this.headerEl = this.el.select('.modal-header',true).first();
2676         this.titleEl = this.el.select('.modal-title',true).first();
2677         this.footerEl = this.el.select('.modal-footer',true).first();
2678
2679         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2680         
2681         //this.el.addClass("x-dlg-modal");
2682
2683         if (this.buttons.length) {
2684             Roo.each(this.buttons, function(bb) {
2685                 var b = Roo.apply({}, bb);
2686                 b.xns = b.xns || Roo.bootstrap;
2687                 b.xtype = b.xtype || 'Button';
2688                 if (typeof(b.listeners) == 'undefined') {
2689                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2690                 }
2691
2692                 var btn = Roo.factory(b);
2693
2694                 btn.render(this.el.select('.modal-footer div').first());
2695
2696             },this);
2697         }
2698         // render the children.
2699         var nitems = [];
2700
2701         if(typeof(this.items) != 'undefined'){
2702             var items = this.items;
2703             delete this.items;
2704
2705             for(var i =0;i < items.length;i++) {
2706                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2707             }
2708         }
2709
2710         this.items = nitems;
2711
2712         // where are these used - they used to be body/close/footer
2713
2714
2715         this.initEvents();
2716         //this.el.addClass([this.fieldClass, this.cls]);
2717
2718     },
2719
2720     getAutoCreate : function()
2721     {
2722         var bdy = {
2723                 cls : 'modal-body',
2724                 html : this.html || ''
2725         };
2726
2727         var title = {
2728             tag: 'h4',
2729             cls : 'modal-title',
2730             html : this.title
2731         };
2732
2733         if(this.specificTitle){
2734             title = this.title;
2735
2736         };
2737
2738         var header = [];
2739         if (this.allow_close) {
2740             header.push({
2741                 tag: 'button',
2742                 cls : 'close',
2743                 html : '&times'
2744             });
2745         }
2746
2747         header.push(title);
2748
2749         var size = '';
2750
2751         if(this.size.length){
2752             size = 'modal-' + this.size;
2753         }
2754
2755         var modal = {
2756             cls: "modal",
2757              cn : [
2758                 {
2759                     cls: "modal-dialog " + size,
2760                     cn : [
2761                         {
2762                             cls : "modal-content",
2763                             cn : [
2764                                 {
2765                                     cls : 'modal-header',
2766                                     cn : header
2767                                 },
2768                                 bdy,
2769                                 {
2770                                     cls : 'modal-footer',
2771                                     cn : [
2772                                         {
2773                                             tag: 'div',
2774                                             cls: 'btn-' + this.buttonPosition
2775                                         }
2776                                     ]
2777
2778                                 }
2779
2780
2781                             ]
2782
2783                         }
2784                     ]
2785
2786                 }
2787             ]
2788         };
2789
2790         if(this.animate){
2791             modal.cls += ' fade';
2792         }
2793
2794         return modal;
2795
2796     },
2797     getChildContainer : function() {
2798
2799          return this.bodyEl;
2800
2801     },
2802     getButtonContainer : function() {
2803          return this.el.select('.modal-footer div',true).first();
2804
2805     },
2806     initEvents : function()
2807     {
2808         if (this.allow_close) {
2809             this.closeEl.on('click', this.hide, this);
2810         }
2811         Roo.EventManager.onWindowResize(this.resize, this, true);
2812
2813
2814     },
2815
2816     resize : function()
2817     {
2818         this.maskEl.setSize(
2819             Roo.lib.Dom.getViewWidth(true),
2820             Roo.lib.Dom.getViewHeight(true)
2821         );
2822         
2823         if (this.fitwindow) {
2824             this.setSize(
2825                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2826                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2827             );
2828             return;
2829         }
2830         
2831         if(this.max_width !== 0) {
2832             
2833             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2834             
2835             if(this.height) {
2836                 this.setSize(w, this.height);
2837                 return;
2838             }
2839             
2840             if(this.max_height) {
2841                 this.setSize(w,Math.min(
2842                     this.max_height,
2843                     Roo.lib.Dom.getViewportHeight(true) - 60
2844                 ));
2845                 
2846                 return;
2847             }
2848             
2849             if(!this.fit_content) {
2850                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2851                 return;
2852             }
2853             
2854             this.setSize(w, Math.min(
2855                 60 +
2856                 this.headerEl.getHeight() + 
2857                 this.footerEl.getHeight() + 
2858                 this.getChildHeight(this.bodyEl.dom.childNodes),
2859                 Roo.lib.Dom.getViewportHeight(true) - 60)
2860             );
2861         }
2862         
2863     },
2864
2865     setSize : function(w,h)
2866     {
2867         if (!w && !h) {
2868             return;
2869         }
2870         
2871         this.resizeTo(w,h);
2872     },
2873
2874     show : function() {
2875
2876         if (!this.rendered) {
2877             this.render();
2878         }
2879
2880         //this.el.setStyle('display', 'block');
2881         this.el.removeClass('hideing');        
2882         this.el.addClass('show');
2883  
2884         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2885             var _this = this;
2886             (function(){
2887                 this.el.addClass('in');
2888             }).defer(50, this);
2889         }else{
2890             this.el.addClass('in');
2891         }
2892
2893         // not sure how we can show data in here..
2894         //if (this.tmpl) {
2895         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2896         //}
2897
2898         Roo.get(document.body).addClass("x-body-masked");
2899         
2900         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2901         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2902         this.maskEl.addClass('show');
2903         
2904         this.resize();
2905         
2906         this.fireEvent('show', this);
2907
2908         // set zindex here - otherwise it appears to be ignored...
2909         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2910
2911         (function () {
2912             this.items.forEach( function(e) {
2913                 e.layout ? e.layout() : false;
2914
2915             });
2916         }).defer(100,this);
2917
2918     },
2919     hide : function()
2920     {
2921         if(this.fireEvent("beforehide", this) !== false){
2922             this.maskEl.removeClass('show');
2923             Roo.get(document.body).removeClass("x-body-masked");
2924             this.el.removeClass('in');
2925             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2926
2927             if(this.animate){ // why
2928                 this.el.addClass('hideing');
2929                 (function(){
2930                     if (!this.el.hasClass('hideing')) {
2931                         return; // it's been shown again...
2932                     }
2933                     this.el.removeClass('show');
2934                     this.el.removeClass('hideing');
2935                 }).defer(150,this);
2936                 
2937             }else{
2938                  this.el.removeClass('show');
2939             }
2940             this.fireEvent('hide', this);
2941         }
2942     },
2943     isVisible : function()
2944     {
2945         
2946         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2947         
2948     },
2949
2950     addButton : function(str, cb)
2951     {
2952
2953
2954         var b = Roo.apply({}, { html : str } );
2955         b.xns = b.xns || Roo.bootstrap;
2956         b.xtype = b.xtype || 'Button';
2957         if (typeof(b.listeners) == 'undefined') {
2958             b.listeners = { click : cb.createDelegate(this)  };
2959         }
2960
2961         var btn = Roo.factory(b);
2962
2963         btn.render(this.el.select('.modal-footer div').first());
2964
2965         return btn;
2966
2967     },
2968
2969     setDefaultButton : function(btn)
2970     {
2971         //this.el.select('.modal-footer').()
2972     },
2973     diff : false,
2974
2975     resizeTo: function(w,h)
2976     {
2977         // skip.. ?? why??
2978
2979         this.dialogEl.setWidth(w);
2980         if (this.diff === false) {
2981             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2982         }
2983
2984         this.bodyEl.setHeight(h - this.diff);
2985
2986         this.fireEvent('resize', this);
2987
2988     },
2989     setContentSize  : function(w, h)
2990     {
2991
2992     },
2993     onButtonClick: function(btn,e)
2994     {
2995         //Roo.log([a,b,c]);
2996         this.fireEvent('btnclick', btn.name, e);
2997     },
2998      /**
2999      * Set the title of the Dialog
3000      * @param {String} str new Title
3001      */
3002     setTitle: function(str) {
3003         this.titleEl.dom.innerHTML = str;
3004     },
3005     /**
3006      * Set the body of the Dialog
3007      * @param {String} str new Title
3008      */
3009     setBody: function(str) {
3010         this.bodyEl.dom.innerHTML = str;
3011     },
3012     /**
3013      * Set the body of the Dialog using the template
3014      * @param {Obj} data - apply this data to the template and replace the body contents.
3015      */
3016     applyBody: function(obj)
3017     {
3018         if (!this.tmpl) {
3019             Roo.log("Error - using apply Body without a template");
3020             //code
3021         }
3022         this.tmpl.overwrite(this.bodyEl, obj);
3023     },
3024     
3025     getChildHeight : function(child_nodes)
3026     {
3027         if(
3028             !child_nodes ||
3029             child_nodes.length == 0
3030         ) {
3031             return;
3032         }
3033         
3034         var child_height = 0;
3035         
3036         for(var i = 0; i < child_nodes.length; i++) {
3037             
3038             /*
3039             * for modal with tabs...
3040             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3041                 
3042                 var layout_childs = child_nodes[i].childNodes;
3043                 
3044                 for(var j = 0; j < layout_childs.length; j++) {
3045                     
3046                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3047                         
3048                         var layout_body_childs = layout_childs[j].childNodes;
3049                         
3050                         for(var k = 0; k < layout_body_childs.length; k++) {
3051                             
3052                             if(layout_body_childs[k].classList.contains('navbar')) {
3053                                 child_height += layout_body_childs[k].offsetHeight;
3054                                 continue;
3055                             }
3056                             
3057                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3058                                 
3059                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3060                                 
3061                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3062                                     
3063                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3064                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3065                                         continue;
3066                                     }
3067                                     
3068                                 }
3069                                 
3070                             }
3071                             
3072                         }
3073                     }
3074                 }
3075                 continue;
3076             }
3077             */
3078             
3079             child_height += child_nodes[i].offsetHeight;
3080             // Roo.log(child_nodes[i].offsetHeight);
3081         }
3082         
3083         return child_height;
3084     }
3085
3086 });
3087
3088
3089 Roo.apply(Roo.bootstrap.Modal,  {
3090     /**
3091          * Button config that displays a single OK button
3092          * @type Object
3093          */
3094         OK :  [{
3095             name : 'ok',
3096             weight : 'primary',
3097             html : 'OK'
3098         }],
3099         /**
3100          * Button config that displays Yes and No buttons
3101          * @type Object
3102          */
3103         YESNO : [
3104             {
3105                 name  : 'no',
3106                 html : 'No'
3107             },
3108             {
3109                 name  :'yes',
3110                 weight : 'primary',
3111                 html : 'Yes'
3112             }
3113         ],
3114
3115         /**
3116          * Button config that displays OK and Cancel buttons
3117          * @type Object
3118          */
3119         OKCANCEL : [
3120             {
3121                name : 'cancel',
3122                 html : 'Cancel'
3123             },
3124             {
3125                 name : 'ok',
3126                 weight : 'primary',
3127                 html : 'OK'
3128             }
3129         ],
3130         /**
3131          * Button config that displays Yes, No and Cancel buttons
3132          * @type Object
3133          */
3134         YESNOCANCEL : [
3135             {
3136                 name : 'yes',
3137                 weight : 'primary',
3138                 html : 'Yes'
3139             },
3140             {
3141                 name : 'no',
3142                 html : 'No'
3143             },
3144             {
3145                 name : 'cancel',
3146                 html : 'Cancel'
3147             }
3148         ],
3149         
3150         zIndex : 10001
3151 });
3152 /*
3153  * - LGPL
3154  *
3155  * messagebox - can be used as a replace
3156  * 
3157  */
3158 /**
3159  * @class Roo.MessageBox
3160  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3161  * Example usage:
3162  *<pre><code>
3163 // Basic alert:
3164 Roo.Msg.alert('Status', 'Changes saved successfully.');
3165
3166 // Prompt for user data:
3167 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3168     if (btn == 'ok'){
3169         // process text value...
3170     }
3171 });
3172
3173 // Show a dialog using config options:
3174 Roo.Msg.show({
3175    title:'Save Changes?',
3176    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3177    buttons: Roo.Msg.YESNOCANCEL,
3178    fn: processResult,
3179    animEl: 'elId'
3180 });
3181 </code></pre>
3182  * @singleton
3183  */
3184 Roo.bootstrap.MessageBox = function(){
3185     var dlg, opt, mask, waitTimer;
3186     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3187     var buttons, activeTextEl, bwidth;
3188
3189     
3190     // private
3191     var handleButton = function(button){
3192         dlg.hide();
3193         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3194     };
3195
3196     // private
3197     var handleHide = function(){
3198         if(opt && opt.cls){
3199             dlg.el.removeClass(opt.cls);
3200         }
3201         //if(waitTimer){
3202         //    Roo.TaskMgr.stop(waitTimer);
3203         //    waitTimer = null;
3204         //}
3205     };
3206
3207     // private
3208     var updateButtons = function(b){
3209         var width = 0;
3210         if(!b){
3211             buttons["ok"].hide();
3212             buttons["cancel"].hide();
3213             buttons["yes"].hide();
3214             buttons["no"].hide();
3215             //dlg.footer.dom.style.display = 'none';
3216             return width;
3217         }
3218         dlg.footerEl.dom.style.display = '';
3219         for(var k in buttons){
3220             if(typeof buttons[k] != "function"){
3221                 if(b[k]){
3222                     buttons[k].show();
3223                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3224                     width += buttons[k].el.getWidth()+15;
3225                 }else{
3226                     buttons[k].hide();
3227                 }
3228             }
3229         }
3230         return width;
3231     };
3232
3233     // private
3234     var handleEsc = function(d, k, e){
3235         if(opt && opt.closable !== false){
3236             dlg.hide();
3237         }
3238         if(e){
3239             e.stopEvent();
3240         }
3241     };
3242
3243     return {
3244         /**
3245          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3246          * @return {Roo.BasicDialog} The BasicDialog element
3247          */
3248         getDialog : function(){
3249            if(!dlg){
3250                 dlg = new Roo.bootstrap.Modal( {
3251                     //draggable: true,
3252                     //resizable:false,
3253                     //constraintoviewport:false,
3254                     //fixedcenter:true,
3255                     //collapsible : false,
3256                     //shim:true,
3257                     //modal: true,
3258                 //    width: 'auto',
3259                   //  height:100,
3260                     //buttonAlign:"center",
3261                     closeClick : function(){
3262                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3263                             handleButton("no");
3264                         }else{
3265                             handleButton("cancel");
3266                         }
3267                     }
3268                 });
3269                 dlg.render();
3270                 dlg.on("hide", handleHide);
3271                 mask = dlg.mask;
3272                 //dlg.addKeyListener(27, handleEsc);
3273                 buttons = {};
3274                 this.buttons = buttons;
3275                 var bt = this.buttonText;
3276                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3277                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3278                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3279                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3280                 //Roo.log(buttons);
3281                 bodyEl = dlg.bodyEl.createChild({
3282
3283                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3284                         '<textarea class="roo-mb-textarea"></textarea>' +
3285                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3286                 });
3287                 msgEl = bodyEl.dom.firstChild;
3288                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3289                 textboxEl.enableDisplayMode();
3290                 textboxEl.addKeyListener([10,13], function(){
3291                     if(dlg.isVisible() && opt && opt.buttons){
3292                         if(opt.buttons.ok){
3293                             handleButton("ok");
3294                         }else if(opt.buttons.yes){
3295                             handleButton("yes");
3296                         }
3297                     }
3298                 });
3299                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3300                 textareaEl.enableDisplayMode();
3301                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3302                 progressEl.enableDisplayMode();
3303                 
3304                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3305                 var pf = progressEl.dom.firstChild;
3306                 if (pf) {
3307                     pp = Roo.get(pf.firstChild);
3308                     pp.setHeight(pf.offsetHeight);
3309                 }
3310                 
3311             }
3312             return dlg;
3313         },
3314
3315         /**
3316          * Updates the message box body text
3317          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3318          * the XHTML-compliant non-breaking space character '&amp;#160;')
3319          * @return {Roo.MessageBox} This message box
3320          */
3321         updateText : function(text)
3322         {
3323             if(!dlg.isVisible() && !opt.width){
3324                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3325                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3326             }
3327             msgEl.innerHTML = text || '&#160;';
3328       
3329             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3330             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3331             var w = Math.max(
3332                     Math.min(opt.width || cw , this.maxWidth), 
3333                     Math.max(opt.minWidth || this.minWidth, bwidth)
3334             );
3335             if(opt.prompt){
3336                 activeTextEl.setWidth(w);
3337             }
3338             if(dlg.isVisible()){
3339                 dlg.fixedcenter = false;
3340             }
3341             // to big, make it scroll. = But as usual stupid IE does not support
3342             // !important..
3343             
3344             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3345                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3346                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3347             } else {
3348                 bodyEl.dom.style.height = '';
3349                 bodyEl.dom.style.overflowY = '';
3350             }
3351             if (cw > w) {
3352                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3353             } else {
3354                 bodyEl.dom.style.overflowX = '';
3355             }
3356             
3357             dlg.setContentSize(w, bodyEl.getHeight());
3358             if(dlg.isVisible()){
3359                 dlg.fixedcenter = true;
3360             }
3361             return this;
3362         },
3363
3364         /**
3365          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3366          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3367          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3368          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3369          * @return {Roo.MessageBox} This message box
3370          */
3371         updateProgress : function(value, text){
3372             if(text){
3373                 this.updateText(text);
3374             }
3375             
3376             if (pp) { // weird bug on my firefox - for some reason this is not defined
3377                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3378                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3379             }
3380             return this;
3381         },        
3382
3383         /**
3384          * Returns true if the message box is currently displayed
3385          * @return {Boolean} True if the message box is visible, else false
3386          */
3387         isVisible : function(){
3388             return dlg && dlg.isVisible();  
3389         },
3390
3391         /**
3392          * Hides the message box if it is displayed
3393          */
3394         hide : function(){
3395             if(this.isVisible()){
3396                 dlg.hide();
3397             }  
3398         },
3399
3400         /**
3401          * Displays a new message box, or reinitializes an existing message box, based on the config options
3402          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3403          * The following config object properties are supported:
3404          * <pre>
3405 Property    Type             Description
3406 ----------  ---------------  ------------------------------------------------------------------------------------
3407 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3408                                    closes (defaults to undefined)
3409 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3410                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3411 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3412                                    progress and wait dialogs will ignore this property and always hide the
3413                                    close button as they can only be closed programmatically.
3414 cls               String           A custom CSS class to apply to the message box element
3415 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3416                                    displayed (defaults to 75)
3417 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3418                                    function will be btn (the name of the button that was clicked, if applicable,
3419                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3420                                    Progress and wait dialogs will ignore this option since they do not respond to
3421                                    user actions and can only be closed programmatically, so any required function
3422                                    should be called by the same code after it closes the dialog.
3423 icon              String           A CSS class that provides a background image to be used as an icon for
3424                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3425 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3426 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3427 modal             Boolean          False to allow user interaction with the page while the message box is
3428                                    displayed (defaults to true)
3429 msg               String           A string that will replace the existing message box body text (defaults
3430                                    to the XHTML-compliant non-breaking space character '&#160;')
3431 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3432 progress          Boolean          True to display a progress bar (defaults to false)
3433 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3434 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3435 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3436 title             String           The title text
3437 value             String           The string value to set into the active textbox element if displayed
3438 wait              Boolean          True to display a progress bar (defaults to false)
3439 width             Number           The width of the dialog in pixels
3440 </pre>
3441          *
3442          * Example usage:
3443          * <pre><code>
3444 Roo.Msg.show({
3445    title: 'Address',
3446    msg: 'Please enter your address:',
3447    width: 300,
3448    buttons: Roo.MessageBox.OKCANCEL,
3449    multiline: true,
3450    fn: saveAddress,
3451    animEl: 'addAddressBtn'
3452 });
3453 </code></pre>
3454          * @param {Object} config Configuration options
3455          * @return {Roo.MessageBox} This message box
3456          */
3457         show : function(options)
3458         {
3459             
3460             // this causes nightmares if you show one dialog after another
3461             // especially on callbacks..
3462              
3463             if(this.isVisible()){
3464                 
3465                 this.hide();
3466                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3467                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3468                 Roo.log("New Dialog Message:" +  options.msg )
3469                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3470                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3471                 
3472             }
3473             var d = this.getDialog();
3474             opt = options;
3475             d.setTitle(opt.title || "&#160;");
3476             d.closeEl.setDisplayed(opt.closable !== false);
3477             activeTextEl = textboxEl;
3478             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3479             if(opt.prompt){
3480                 if(opt.multiline){
3481                     textboxEl.hide();
3482                     textareaEl.show();
3483                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3484                         opt.multiline : this.defaultTextHeight);
3485                     activeTextEl = textareaEl;
3486                 }else{
3487                     textboxEl.show();
3488                     textareaEl.hide();
3489                 }
3490             }else{
3491                 textboxEl.hide();
3492                 textareaEl.hide();
3493             }
3494             progressEl.setDisplayed(opt.progress === true);
3495             this.updateProgress(0);
3496             activeTextEl.dom.value = opt.value || "";
3497             if(opt.prompt){
3498                 dlg.setDefaultButton(activeTextEl);
3499             }else{
3500                 var bs = opt.buttons;
3501                 var db = null;
3502                 if(bs && bs.ok){
3503                     db = buttons["ok"];
3504                 }else if(bs && bs.yes){
3505                     db = buttons["yes"];
3506                 }
3507                 dlg.setDefaultButton(db);
3508             }
3509             bwidth = updateButtons(opt.buttons);
3510             this.updateText(opt.msg);
3511             if(opt.cls){
3512                 d.el.addClass(opt.cls);
3513             }
3514             d.proxyDrag = opt.proxyDrag === true;
3515             d.modal = opt.modal !== false;
3516             d.mask = opt.modal !== false ? mask : false;
3517             if(!d.isVisible()){
3518                 // force it to the end of the z-index stack so it gets a cursor in FF
3519                 document.body.appendChild(dlg.el.dom);
3520                 d.animateTarget = null;
3521                 d.show(options.animEl);
3522             }
3523             return this;
3524         },
3525
3526         /**
3527          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3528          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3529          * and closing the message box when the process is complete.
3530          * @param {String} title The title bar text
3531          * @param {String} msg The message box body text
3532          * @return {Roo.MessageBox} This message box
3533          */
3534         progress : function(title, msg){
3535             this.show({
3536                 title : title,
3537                 msg : msg,
3538                 buttons: false,
3539                 progress:true,
3540                 closable:false,
3541                 minWidth: this.minProgressWidth,
3542                 modal : true
3543             });
3544             return this;
3545         },
3546
3547         /**
3548          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3549          * If a callback function is passed it will be called after the user clicks the button, and the
3550          * id of the button that was clicked will be passed as the only parameter to the callback
3551          * (could also be the top-right close button).
3552          * @param {String} title The title bar text
3553          * @param {String} msg The message box body text
3554          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3555          * @param {Object} scope (optional) The scope of the callback function
3556          * @return {Roo.MessageBox} This message box
3557          */
3558         alert : function(title, msg, fn, scope)
3559         {
3560             this.show({
3561                 title : title,
3562                 msg : msg,
3563                 buttons: this.OK,
3564                 fn: fn,
3565                 closable : false,
3566                 scope : scope,
3567                 modal : true
3568             });
3569             return this;
3570         },
3571
3572         /**
3573          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3574          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3575          * You are responsible for closing the message box when the process is complete.
3576          * @param {String} msg The message box body text
3577          * @param {String} title (optional) The title bar text
3578          * @return {Roo.MessageBox} This message box
3579          */
3580         wait : function(msg, title){
3581             this.show({
3582                 title : title,
3583                 msg : msg,
3584                 buttons: false,
3585                 closable:false,
3586                 progress:true,
3587                 modal:true,
3588                 width:300,
3589                 wait:true
3590             });
3591             waitTimer = Roo.TaskMgr.start({
3592                 run: function(i){
3593                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3594                 },
3595                 interval: 1000
3596             });
3597             return this;
3598         },
3599
3600         /**
3601          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3602          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3603          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3604          * @param {String} title The title bar text
3605          * @param {String} msg The message box body text
3606          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3607          * @param {Object} scope (optional) The scope of the callback function
3608          * @return {Roo.MessageBox} This message box
3609          */
3610         confirm : function(title, msg, fn, scope){
3611             this.show({
3612                 title : title,
3613                 msg : msg,
3614                 buttons: this.YESNO,
3615                 fn: fn,
3616                 scope : scope,
3617                 modal : true
3618             });
3619             return this;
3620         },
3621
3622         /**
3623          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3624          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3625          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3626          * (could also be the top-right close button) and the text that was entered will be passed as the two
3627          * parameters to the callback.
3628          * @param {String} title The title bar text
3629          * @param {String} msg The message box body text
3630          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3631          * @param {Object} scope (optional) The scope of the callback function
3632          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3633          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3634          * @return {Roo.MessageBox} This message box
3635          */
3636         prompt : function(title, msg, fn, scope, multiline){
3637             this.show({
3638                 title : title,
3639                 msg : msg,
3640                 buttons: this.OKCANCEL,
3641                 fn: fn,
3642                 minWidth:250,
3643                 scope : scope,
3644                 prompt:true,
3645                 multiline: multiline,
3646                 modal : true
3647             });
3648             return this;
3649         },
3650
3651         /**
3652          * Button config that displays a single OK button
3653          * @type Object
3654          */
3655         OK : {ok:true},
3656         /**
3657          * Button config that displays Yes and No buttons
3658          * @type Object
3659          */
3660         YESNO : {yes:true, no:true},
3661         /**
3662          * Button config that displays OK and Cancel buttons
3663          * @type Object
3664          */
3665         OKCANCEL : {ok:true, cancel:true},
3666         /**
3667          * Button config that displays Yes, No and Cancel buttons
3668          * @type Object
3669          */
3670         YESNOCANCEL : {yes:true, no:true, cancel:true},
3671
3672         /**
3673          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3674          * @type Number
3675          */
3676         defaultTextHeight : 75,
3677         /**
3678          * The maximum width in pixels of the message box (defaults to 600)
3679          * @type Number
3680          */
3681         maxWidth : 600,
3682         /**
3683          * The minimum width in pixels of the message box (defaults to 100)
3684          * @type Number
3685          */
3686         minWidth : 100,
3687         /**
3688          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3689          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3690          * @type Number
3691          */
3692         minProgressWidth : 250,
3693         /**
3694          * An object containing the default button text strings that can be overriden for localized language support.
3695          * Supported properties are: ok, cancel, yes and no.
3696          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3697          * @type Object
3698          */
3699         buttonText : {
3700             ok : "OK",
3701             cancel : "Cancel",
3702             yes : "Yes",
3703             no : "No"
3704         }
3705     };
3706 }();
3707
3708 /**
3709  * Shorthand for {@link Roo.MessageBox}
3710  */
3711 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3712 Roo.Msg = Roo.Msg || Roo.MessageBox;
3713 /*
3714  * - LGPL
3715  *
3716  * navbar
3717  * 
3718  */
3719
3720 /**
3721  * @class Roo.bootstrap.Navbar
3722  * @extends Roo.bootstrap.Component
3723  * Bootstrap Navbar class
3724
3725  * @constructor
3726  * Create a new Navbar
3727  * @param {Object} config The config object
3728  */
3729
3730
3731 Roo.bootstrap.Navbar = function(config){
3732     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3733     this.addEvents({
3734         // raw events
3735         /**
3736          * @event beforetoggle
3737          * Fire before toggle the menu
3738          * @param {Roo.EventObject} e
3739          */
3740         "beforetoggle" : true
3741     });
3742 };
3743
3744 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3745     
3746     
3747    
3748     // private
3749     navItems : false,
3750     loadMask : false,
3751     
3752     
3753     getAutoCreate : function(){
3754         
3755         
3756         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3757         
3758     },
3759     
3760     initEvents :function ()
3761     {
3762         //Roo.log(this.el.select('.navbar-toggle',true));
3763         this.el.select('.navbar-toggle',true).on('click', function() {
3764             if(this.fireEvent('beforetoggle', this) !== false){
3765                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3766             }
3767             
3768         }, this);
3769         
3770         var mark = {
3771             tag: "div",
3772             cls:"x-dlg-mask"
3773         };
3774         
3775         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3776         
3777         var size = this.el.getSize();
3778         this.maskEl.setSize(size.width, size.height);
3779         this.maskEl.enableDisplayMode("block");
3780         this.maskEl.hide();
3781         
3782         if(this.loadMask){
3783             this.maskEl.show();
3784         }
3785     },
3786     
3787     
3788     getChildContainer : function()
3789     {
3790         if (this.el.select('.collapse').getCount()) {
3791             return this.el.select('.collapse',true).first();
3792         }
3793         
3794         return this.el;
3795     },
3796     
3797     mask : function()
3798     {
3799         this.maskEl.show();
3800     },
3801     
3802     unmask : function()
3803     {
3804         this.maskEl.hide();
3805     } 
3806     
3807     
3808     
3809     
3810 });
3811
3812
3813
3814  
3815
3816  /*
3817  * - LGPL
3818  *
3819  * navbar
3820  * 
3821  */
3822
3823 /**
3824  * @class Roo.bootstrap.NavSimplebar
3825  * @extends Roo.bootstrap.Navbar
3826  * Bootstrap Sidebar class
3827  *
3828  * @cfg {Boolean} inverse is inverted color
3829  * 
3830  * @cfg {String} type (nav | pills | tabs)
3831  * @cfg {Boolean} arrangement stacked | justified
3832  * @cfg {String} align (left | right) alignment
3833  * 
3834  * @cfg {Boolean} main (true|false) main nav bar? default false
3835  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3836  * 
3837  * @cfg {String} tag (header|footer|nav|div) default is nav 
3838
3839  * 
3840  * 
3841  * 
3842  * @constructor
3843  * Create a new Sidebar
3844  * @param {Object} config The config object
3845  */
3846
3847
3848 Roo.bootstrap.NavSimplebar = function(config){
3849     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3850 };
3851
3852 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3853     
3854     inverse: false,
3855     
3856     type: false,
3857     arrangement: '',
3858     align : false,
3859     
3860     
3861     
3862     main : false,
3863     
3864     
3865     tag : false,
3866     
3867     
3868     getAutoCreate : function(){
3869         
3870         
3871         var cfg = {
3872             tag : this.tag || 'div',
3873             cls : 'navbar'
3874         };
3875           
3876         
3877         cfg.cn = [
3878             {
3879                 cls: 'nav',
3880                 tag : 'ul'
3881             }
3882         ];
3883         
3884          
3885         this.type = this.type || 'nav';
3886         if (['tabs','pills'].indexOf(this.type)!==-1) {
3887             cfg.cn[0].cls += ' nav-' + this.type
3888         
3889         
3890         } else {
3891             if (this.type!=='nav') {
3892                 Roo.log('nav type must be nav/tabs/pills')
3893             }
3894             cfg.cn[0].cls += ' navbar-nav'
3895         }
3896         
3897         
3898         
3899         
3900         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3901             cfg.cn[0].cls += ' nav-' + this.arrangement;
3902         }
3903         
3904         
3905         if (this.align === 'right') {
3906             cfg.cn[0].cls += ' navbar-right';
3907         }
3908         
3909         if (this.inverse) {
3910             cfg.cls += ' navbar-inverse';
3911             
3912         }
3913         
3914         
3915         return cfg;
3916     
3917         
3918     }
3919     
3920     
3921     
3922 });
3923
3924
3925
3926  
3927
3928  
3929        /*
3930  * - LGPL
3931  *
3932  * navbar
3933  * 
3934  */
3935
3936 /**
3937  * @class Roo.bootstrap.NavHeaderbar
3938  * @extends Roo.bootstrap.NavSimplebar
3939  * Bootstrap Sidebar class
3940  *
3941  * @cfg {String} brand what is brand
3942  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3943  * @cfg {String} brand_href href of the brand
3944  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3945  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3946  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3947  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3948  * 
3949  * @constructor
3950  * Create a new Sidebar
3951  * @param {Object} config The config object
3952  */
3953
3954
3955 Roo.bootstrap.NavHeaderbar = function(config){
3956     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3957       
3958 };
3959
3960 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3961     
3962     position: '',
3963     brand: '',
3964     brand_href: false,
3965     srButton : true,
3966     autohide : false,
3967     desktopCenter : false,
3968    
3969     
3970     getAutoCreate : function(){
3971         
3972         var   cfg = {
3973             tag: this.nav || 'nav',
3974             cls: 'navbar',
3975             role: 'navigation',
3976             cn: []
3977         };
3978         
3979         var cn = cfg.cn;
3980         if (this.desktopCenter) {
3981             cn.push({cls : 'container', cn : []});
3982             cn = cn[0].cn;
3983         }
3984         
3985         if(this.srButton){
3986             cn.push({
3987                 tag: 'div',
3988                 cls: 'navbar-header',
3989                 cn: [
3990                     {
3991                         tag: 'button',
3992                         type: 'button',
3993                         cls: 'navbar-toggle',
3994                         'data-toggle': 'collapse',
3995                         cn: [
3996                             {
3997                                 tag: 'span',
3998                                 cls: 'sr-only',
3999                                 html: 'Toggle navigation'
4000                             },
4001                             {
4002                                 tag: 'span',
4003                                 cls: 'icon-bar'
4004                             },
4005                             {
4006                                 tag: 'span',
4007                                 cls: 'icon-bar'
4008                             },
4009                             {
4010                                 tag: 'span',
4011                                 cls: 'icon-bar'
4012                             }
4013                         ]
4014                     }
4015                 ]
4016             });
4017         }
4018         
4019         cn.push({
4020             tag: 'div',
4021             cls: 'collapse navbar-collapse',
4022             cn : []
4023         });
4024         
4025         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4026         
4027         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4028             cfg.cls += ' navbar-' + this.position;
4029             
4030             // tag can override this..
4031             
4032             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4033         }
4034         
4035         if (this.brand !== '') {
4036             cn[0].cn.push({
4037                 tag: 'a',
4038                 href: this.brand_href ? this.brand_href : '#',
4039                 cls: 'navbar-brand',
4040                 cn: [
4041                 this.brand
4042                 ]
4043             });
4044         }
4045         
4046         if(this.main){
4047             cfg.cls += ' main-nav';
4048         }
4049         
4050         
4051         return cfg;
4052
4053         
4054     },
4055     getHeaderChildContainer : function()
4056     {
4057         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4058             return this.el.select('.navbar-header',true).first();
4059         }
4060         
4061         return this.getChildContainer();
4062     },
4063     
4064     
4065     initEvents : function()
4066     {
4067         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4068         
4069         if (this.autohide) {
4070             
4071             var prevScroll = 0;
4072             var ft = this.el;
4073             
4074             Roo.get(document).on('scroll',function(e) {
4075                 var ns = Roo.get(document).getScroll().top;
4076                 var os = prevScroll;
4077                 prevScroll = ns;
4078                 
4079                 if(ns > os){
4080                     ft.removeClass('slideDown');
4081                     ft.addClass('slideUp');
4082                     return;
4083                 }
4084                 ft.removeClass('slideUp');
4085                 ft.addClass('slideDown');
4086                  
4087               
4088           },this);
4089         }
4090     }    
4091     
4092 });
4093
4094
4095
4096  
4097
4098  /*
4099  * - LGPL
4100  *
4101  * navbar
4102  * 
4103  */
4104
4105 /**
4106  * @class Roo.bootstrap.NavSidebar
4107  * @extends Roo.bootstrap.Navbar
4108  * Bootstrap Sidebar class
4109  * 
4110  * @constructor
4111  * Create a new Sidebar
4112  * @param {Object} config The config object
4113  */
4114
4115
4116 Roo.bootstrap.NavSidebar = function(config){
4117     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4118 };
4119
4120 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4121     
4122     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4123     
4124     getAutoCreate : function(){
4125         
4126         
4127         return  {
4128             tag: 'div',
4129             cls: 'sidebar sidebar-nav'
4130         };
4131     
4132         
4133     }
4134     
4135     
4136     
4137 });
4138
4139
4140
4141  
4142
4143  /*
4144  * - LGPL
4145  *
4146  * nav group
4147  * 
4148  */
4149
4150 /**
4151  * @class Roo.bootstrap.NavGroup
4152  * @extends Roo.bootstrap.Component
4153  * Bootstrap NavGroup class
4154  * @cfg {String} align (left|right)
4155  * @cfg {Boolean} inverse
4156  * @cfg {String} type (nav|pills|tab) default nav
4157  * @cfg {String} navId - reference Id for navbar.
4158
4159  * 
4160  * @constructor
4161  * Create a new nav group
4162  * @param {Object} config The config object
4163  */
4164
4165 Roo.bootstrap.NavGroup = function(config){
4166     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4167     this.navItems = [];
4168    
4169     Roo.bootstrap.NavGroup.register(this);
4170      this.addEvents({
4171         /**
4172              * @event changed
4173              * Fires when the active item changes
4174              * @param {Roo.bootstrap.NavGroup} this
4175              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4176              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4177          */
4178         'changed': true
4179      });
4180     
4181 };
4182
4183 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4184     
4185     align: '',
4186     inverse: false,
4187     form: false,
4188     type: 'nav',
4189     navId : '',
4190     // private
4191     
4192     navItems : false, 
4193     
4194     getAutoCreate : function()
4195     {
4196         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4197         
4198         cfg = {
4199             tag : 'ul',
4200             cls: 'nav' 
4201         };
4202         
4203         if (['tabs','pills'].indexOf(this.type)!==-1) {
4204             cfg.cls += ' nav-' + this.type
4205         } else {
4206             if (this.type!=='nav') {
4207                 Roo.log('nav type must be nav/tabs/pills')
4208             }
4209             cfg.cls += ' navbar-nav'
4210         }
4211         
4212         if (this.parent() && this.parent().sidebar) {
4213             cfg = {
4214                 tag: 'ul',
4215                 cls: 'dashboard-menu sidebar-menu'
4216             };
4217             
4218             return cfg;
4219         }
4220         
4221         if (this.form === true) {
4222             cfg = {
4223                 tag: 'form',
4224                 cls: 'navbar-form'
4225             };
4226             
4227             if (this.align === 'right') {
4228                 cfg.cls += ' navbar-right';
4229             } else {
4230                 cfg.cls += ' navbar-left';
4231             }
4232         }
4233         
4234         if (this.align === 'right') {
4235             cfg.cls += ' navbar-right';
4236         }
4237         
4238         if (this.inverse) {
4239             cfg.cls += ' navbar-inverse';
4240             
4241         }
4242         
4243         
4244         return cfg;
4245     },
4246     /**
4247     * sets the active Navigation item
4248     * @param {Roo.bootstrap.NavItem} the new current navitem
4249     */
4250     setActiveItem : function(item)
4251     {
4252         var prev = false;
4253         Roo.each(this.navItems, function(v){
4254             if (v == item) {
4255                 return ;
4256             }
4257             if (v.isActive()) {
4258                 v.setActive(false, true);
4259                 prev = v;
4260                 
4261             }
4262             
4263         });
4264
4265         item.setActive(true, true);
4266         this.fireEvent('changed', this, item, prev);
4267         
4268         
4269     },
4270     /**
4271     * gets the active Navigation item
4272     * @return {Roo.bootstrap.NavItem} the current navitem
4273     */
4274     getActive : function()
4275     {
4276         
4277         var prev = false;
4278         Roo.each(this.navItems, function(v){
4279             
4280             if (v.isActive()) {
4281                 prev = v;
4282                 
4283             }
4284             
4285         });
4286         return prev;
4287     },
4288     
4289     indexOfNav : function()
4290     {
4291         
4292         var prev = false;
4293         Roo.each(this.navItems, function(v,i){
4294             
4295             if (v.isActive()) {
4296                 prev = i;
4297                 
4298             }
4299             
4300         });
4301         return prev;
4302     },
4303     /**
4304     * adds a Navigation item
4305     * @param {Roo.bootstrap.NavItem} the navitem to add
4306     */
4307     addItem : function(cfg)
4308     {
4309         var cn = new Roo.bootstrap.NavItem(cfg);
4310         this.register(cn);
4311         cn.parentId = this.id;
4312         cn.onRender(this.el, null);
4313         return cn;
4314     },
4315     /**
4316     * register a Navigation item
4317     * @param {Roo.bootstrap.NavItem} the navitem to add
4318     */
4319     register : function(item)
4320     {
4321         this.navItems.push( item);
4322         item.navId = this.navId;
4323     
4324     },
4325     
4326     /**
4327     * clear all the Navigation item
4328     */
4329    
4330     clearAll : function()
4331     {
4332         this.navItems = [];
4333         this.el.dom.innerHTML = '';
4334     },
4335     
4336     getNavItem: function(tabId)
4337     {
4338         var ret = false;
4339         Roo.each(this.navItems, function(e) {
4340             if (e.tabId == tabId) {
4341                ret =  e;
4342                return false;
4343             }
4344             return true;
4345             
4346         });
4347         return ret;
4348     },
4349     
4350     setActiveNext : function()
4351     {
4352         var i = this.indexOfNav(this.getActive());
4353         if (i > this.navItems.length) {
4354             return;
4355         }
4356         this.setActiveItem(this.navItems[i+1]);
4357     },
4358     setActivePrev : function()
4359     {
4360         var i = this.indexOfNav(this.getActive());
4361         if (i  < 1) {
4362             return;
4363         }
4364         this.setActiveItem(this.navItems[i-1]);
4365     },
4366     clearWasActive : function(except) {
4367         Roo.each(this.navItems, function(e) {
4368             if (e.tabId != except.tabId && e.was_active) {
4369                e.was_active = false;
4370                return false;
4371             }
4372             return true;
4373             
4374         });
4375     },
4376     getWasActive : function ()
4377     {
4378         var r = false;
4379         Roo.each(this.navItems, function(e) {
4380             if (e.was_active) {
4381                r = e;
4382                return false;
4383             }
4384             return true;
4385             
4386         });
4387         return r;
4388     }
4389     
4390     
4391 });
4392
4393  
4394 Roo.apply(Roo.bootstrap.NavGroup, {
4395     
4396     groups: {},
4397      /**
4398     * register a Navigation Group
4399     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4400     */
4401     register : function(navgrp)
4402     {
4403         this.groups[navgrp.navId] = navgrp;
4404         
4405     },
4406     /**
4407     * fetch a Navigation Group based on the navigation ID
4408     * @param {string} the navgroup to add
4409     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4410     */
4411     get: function(navId) {
4412         if (typeof(this.groups[navId]) == 'undefined') {
4413             return false;
4414             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4415         }
4416         return this.groups[navId] ;
4417     }
4418     
4419     
4420     
4421 });
4422
4423  /*
4424  * - LGPL
4425  *
4426  * row
4427  * 
4428  */
4429
4430 /**
4431  * @class Roo.bootstrap.NavItem
4432  * @extends Roo.bootstrap.Component
4433  * Bootstrap Navbar.NavItem class
4434  * @cfg {String} href  link to
4435  * @cfg {String} html content of button
4436  * @cfg {String} badge text inside badge
4437  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4438  * @cfg {String} glyphicon name of glyphicon
4439  * @cfg {String} icon name of font awesome icon
4440  * @cfg {Boolean} active Is item active
4441  * @cfg {Boolean} disabled Is item disabled
4442  
4443  * @cfg {Boolean} preventDefault (true | false) default false
4444  * @cfg {String} tabId the tab that this item activates.
4445  * @cfg {String} tagtype (a|span) render as a href or span?
4446  * @cfg {Boolean} animateRef (true|false) link to element default false  
4447   
4448  * @constructor
4449  * Create a new Navbar Item
4450  * @param {Object} config The config object
4451  */
4452 Roo.bootstrap.NavItem = function(config){
4453     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4454     this.addEvents({
4455         // raw events
4456         /**
4457          * @event click
4458          * The raw click event for the entire grid.
4459          * @param {Roo.EventObject} e
4460          */
4461         "click" : true,
4462          /**
4463             * @event changed
4464             * Fires when the active item active state changes
4465             * @param {Roo.bootstrap.NavItem} this
4466             * @param {boolean} state the new state
4467              
4468          */
4469         'changed': true,
4470         /**
4471             * @event scrollto
4472             * Fires when scroll to element
4473             * @param {Roo.bootstrap.NavItem} this
4474             * @param {Object} options
4475             * @param {Roo.EventObject} e
4476              
4477          */
4478         'scrollto': true
4479     });
4480    
4481 };
4482
4483 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4484     
4485     href: false,
4486     html: '',
4487     badge: '',
4488     icon: false,
4489     glyphicon: false,
4490     active: false,
4491     preventDefault : false,
4492     tabId : false,
4493     tagtype : 'a',
4494     disabled : false,
4495     animateRef : false,
4496     was_active : false,
4497     
4498     getAutoCreate : function(){
4499          
4500         var cfg = {
4501             tag: 'li',
4502             cls: 'nav-item'
4503             
4504         };
4505         
4506         if (this.active) {
4507             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4508         }
4509         if (this.disabled) {
4510             cfg.cls += ' disabled';
4511         }
4512         
4513         if (this.href || this.html || this.glyphicon || this.icon) {
4514             cfg.cn = [
4515                 {
4516                     tag: this.tagtype,
4517                     href : this.href || "#",
4518                     html: this.html || ''
4519                 }
4520             ];
4521             
4522             if (this.icon) {
4523                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4524             }
4525
4526             if(this.glyphicon) {
4527                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4528             }
4529             
4530             if (this.menu) {
4531                 
4532                 cfg.cn[0].html += " <span class='caret'></span>";
4533              
4534             }
4535             
4536             if (this.badge !== '') {
4537                  
4538                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4539             }
4540         }
4541         
4542         
4543         
4544         return cfg;
4545     },
4546     initEvents: function() 
4547     {
4548         if (typeof (this.menu) != 'undefined') {
4549             this.menu.parentType = this.xtype;
4550             this.menu.triggerEl = this.el;
4551             this.menu = this.addxtype(Roo.apply({}, this.menu));
4552         }
4553         
4554         this.el.select('a',true).on('click', this.onClick, this);
4555         
4556         if(this.tagtype == 'span'){
4557             this.el.select('span',true).on('click', this.onClick, this);
4558         }
4559        
4560         // at this point parent should be available..
4561         this.parent().register(this);
4562     },
4563     
4564     onClick : function(e)
4565     {
4566         if (e.getTarget('.dropdown-menu-item')) {
4567             // did you click on a menu itemm.... - then don't trigger onclick..
4568             return;
4569         }
4570         
4571         if(
4572                 this.preventDefault || 
4573                 this.href == '#' 
4574         ){
4575             Roo.log("NavItem - prevent Default?");
4576             e.preventDefault();
4577         }
4578         
4579         if (this.disabled) {
4580             return;
4581         }
4582         
4583         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4584         if (tg && tg.transition) {
4585             Roo.log("waiting for the transitionend");
4586             return;
4587         }
4588         
4589         
4590         
4591         //Roo.log("fire event clicked");
4592         if(this.fireEvent('click', this, e) === false){
4593             return;
4594         };
4595         
4596         if(this.tagtype == 'span'){
4597             return;
4598         }
4599         
4600         //Roo.log(this.href);
4601         var ael = this.el.select('a',true).first();
4602         //Roo.log(ael);
4603         
4604         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4605             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4606             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4607                 return; // ignore... - it's a 'hash' to another page.
4608             }
4609             Roo.log("NavItem - prevent Default?");
4610             e.preventDefault();
4611             this.scrollToElement(e);
4612         }
4613         
4614         
4615         var p =  this.parent();
4616    
4617         if (['tabs','pills'].indexOf(p.type)!==-1) {
4618             if (typeof(p.setActiveItem) !== 'undefined') {
4619                 p.setActiveItem(this);
4620             }
4621         }
4622         
4623         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4624         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4625             // remove the collapsed menu expand...
4626             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4627         }
4628     },
4629     
4630     isActive: function () {
4631         return this.active
4632     },
4633     setActive : function(state, fire, is_was_active)
4634     {
4635         if (this.active && !state && this.navId) {
4636             this.was_active = true;
4637             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4638             if (nv) {
4639                 nv.clearWasActive(this);
4640             }
4641             
4642         }
4643         this.active = state;
4644         
4645         if (!state ) {
4646             this.el.removeClass('active');
4647         } else if (!this.el.hasClass('active')) {
4648             this.el.addClass('active');
4649         }
4650         if (fire) {
4651             this.fireEvent('changed', this, state);
4652         }
4653         
4654         // show a panel if it's registered and related..
4655         
4656         if (!this.navId || !this.tabId || !state || is_was_active) {
4657             return;
4658         }
4659         
4660         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4661         if (!tg) {
4662             return;
4663         }
4664         var pan = tg.getPanelByName(this.tabId);
4665         if (!pan) {
4666             return;
4667         }
4668         // if we can not flip to new panel - go back to old nav highlight..
4669         if (false == tg.showPanel(pan)) {
4670             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4671             if (nv) {
4672                 var onav = nv.getWasActive();
4673                 if (onav) {
4674                     onav.setActive(true, false, true);
4675                 }
4676             }
4677             
4678         }
4679         
4680         
4681         
4682     },
4683      // this should not be here...
4684     setDisabled : function(state)
4685     {
4686         this.disabled = state;
4687         if (!state ) {
4688             this.el.removeClass('disabled');
4689         } else if (!this.el.hasClass('disabled')) {
4690             this.el.addClass('disabled');
4691         }
4692         
4693     },
4694     
4695     /**
4696      * Fetch the element to display the tooltip on.
4697      * @return {Roo.Element} defaults to this.el
4698      */
4699     tooltipEl : function()
4700     {
4701         return this.el.select('' + this.tagtype + '', true).first();
4702     },
4703     
4704     scrollToElement : function(e)
4705     {
4706         var c = document.body;
4707         
4708         /*
4709          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4710          */
4711         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4712             c = document.documentElement;
4713         }
4714         
4715         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4716         
4717         if(!target){
4718             return;
4719         }
4720
4721         var o = target.calcOffsetsTo(c);
4722         
4723         var options = {
4724             target : target,
4725             value : o[1]
4726         };
4727         
4728         this.fireEvent('scrollto', this, options, e);
4729         
4730         Roo.get(c).scrollTo('top', options.value, true);
4731         
4732         return;
4733     }
4734 });
4735  
4736
4737  /*
4738  * - LGPL
4739  *
4740  * sidebar item
4741  *
4742  *  li
4743  *    <span> icon </span>
4744  *    <span> text </span>
4745  *    <span>badge </span>
4746  */
4747
4748 /**
4749  * @class Roo.bootstrap.NavSidebarItem
4750  * @extends Roo.bootstrap.NavItem
4751  * Bootstrap Navbar.NavSidebarItem class
4752  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4753  * {Boolean} open is the menu open
4754  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4755  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4756  * {String} buttonSize (sm|md|lg)the extra classes for the button
4757  * {Boolean} showArrow show arrow next to the text (default true)
4758  * @constructor
4759  * Create a new Navbar Button
4760  * @param {Object} config The config object
4761  */
4762 Roo.bootstrap.NavSidebarItem = function(config){
4763     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4764     this.addEvents({
4765         // raw events
4766         /**
4767          * @event click
4768          * The raw click event for the entire grid.
4769          * @param {Roo.EventObject} e
4770          */
4771         "click" : true,
4772          /**
4773             * @event changed
4774             * Fires when the active item active state changes
4775             * @param {Roo.bootstrap.NavSidebarItem} this
4776             * @param {boolean} state the new state
4777              
4778          */
4779         'changed': true
4780     });
4781    
4782 };
4783
4784 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4785     
4786     badgeWeight : 'default',
4787     
4788     open: false,
4789     
4790     buttonView : false,
4791     
4792     buttonWeight : 'default',
4793     
4794     buttonSize : 'md',
4795     
4796     showArrow : true,
4797     
4798     getAutoCreate : function(){
4799         
4800         
4801         var a = {
4802                 tag: 'a',
4803                 href : this.href || '#',
4804                 cls: '',
4805                 html : '',
4806                 cn : []
4807         };
4808         
4809         if(this.buttonView){
4810             a = {
4811                 tag: 'button',
4812                 href : this.href || '#',
4813                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4814                 html : this.html,
4815                 cn : []
4816             };
4817         }
4818         
4819         var cfg = {
4820             tag: 'li',
4821             cls: '',
4822             cn: [ a ]
4823         };
4824         
4825         if (this.active) {
4826             cfg.cls += ' active';
4827         }
4828         
4829         if (this.disabled) {
4830             cfg.cls += ' disabled';
4831         }
4832         if (this.open) {
4833             cfg.cls += ' open x-open';
4834         }
4835         // left icon..
4836         if (this.glyphicon || this.icon) {
4837             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4838             a.cn.push({ tag : 'i', cls : c }) ;
4839         }
4840         
4841         if(!this.buttonView){
4842             var span = {
4843                 tag: 'span',
4844                 html : this.html || ''
4845             };
4846
4847             a.cn.push(span);
4848             
4849         }
4850         
4851         if (this.badge !== '') {
4852             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4853         }
4854         
4855         if (this.menu) {
4856             
4857             if(this.showArrow){
4858                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4859             }
4860             
4861             a.cls += ' dropdown-toggle treeview' ;
4862         }
4863         
4864         return cfg;
4865     },
4866     
4867     initEvents : function()
4868     { 
4869         if (typeof (this.menu) != 'undefined') {
4870             this.menu.parentType = this.xtype;
4871             this.menu.triggerEl = this.el;
4872             this.menu = this.addxtype(Roo.apply({}, this.menu));
4873         }
4874         
4875         this.el.on('click', this.onClick, this);
4876         
4877         if(this.badge !== ''){
4878             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4879         }
4880         
4881     },
4882     
4883     onClick : function(e)
4884     {
4885         if(this.disabled){
4886             e.preventDefault();
4887             return;
4888         }
4889         
4890         if(this.preventDefault){
4891             e.preventDefault();
4892         }
4893         
4894         this.fireEvent('click', this);
4895     },
4896     
4897     disable : function()
4898     {
4899         this.setDisabled(true);
4900     },
4901     
4902     enable : function()
4903     {
4904         this.setDisabled(false);
4905     },
4906     
4907     setDisabled : function(state)
4908     {
4909         if(this.disabled == state){
4910             return;
4911         }
4912         
4913         this.disabled = state;
4914         
4915         if (state) {
4916             this.el.addClass('disabled');
4917             return;
4918         }
4919         
4920         this.el.removeClass('disabled');
4921         
4922         return;
4923     },
4924     
4925     setActive : function(state)
4926     {
4927         if(this.active == state){
4928             return;
4929         }
4930         
4931         this.active = state;
4932         
4933         if (state) {
4934             this.el.addClass('active');
4935             return;
4936         }
4937         
4938         this.el.removeClass('active');
4939         
4940         return;
4941     },
4942     
4943     isActive: function () 
4944     {
4945         return this.active;
4946     },
4947     
4948     setBadge : function(str)
4949     {
4950         if(!this.badgeEl){
4951             return;
4952         }
4953         
4954         this.badgeEl.dom.innerHTML = str;
4955     }
4956     
4957    
4958      
4959  
4960 });
4961  
4962
4963  /*
4964  * - LGPL
4965  *
4966  * row
4967  * 
4968  */
4969
4970 /**
4971  * @class Roo.bootstrap.Row
4972  * @extends Roo.bootstrap.Component
4973  * Bootstrap Row class (contains columns...)
4974  * 
4975  * @constructor
4976  * Create a new Row
4977  * @param {Object} config The config object
4978  */
4979
4980 Roo.bootstrap.Row = function(config){
4981     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4982 };
4983
4984 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4985     
4986     getAutoCreate : function(){
4987        return {
4988             cls: 'row clearfix'
4989        };
4990     }
4991     
4992     
4993 });
4994
4995  
4996
4997  /*
4998  * - LGPL
4999  *
5000  * element
5001  * 
5002  */
5003
5004 /**
5005  * @class Roo.bootstrap.Element
5006  * @extends Roo.bootstrap.Component
5007  * Bootstrap Element class
5008  * @cfg {String} html contents of the element
5009  * @cfg {String} tag tag of the element
5010  * @cfg {String} cls class of the element
5011  * @cfg {Boolean} preventDefault (true|false) default false
5012  * @cfg {Boolean} clickable (true|false) default false
5013  * 
5014  * @constructor
5015  * Create a new Element
5016  * @param {Object} config The config object
5017  */
5018
5019 Roo.bootstrap.Element = function(config){
5020     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5021     
5022     this.addEvents({
5023         // raw events
5024         /**
5025          * @event click
5026          * When a element is chick
5027          * @param {Roo.bootstrap.Element} this
5028          * @param {Roo.EventObject} e
5029          */
5030         "click" : true
5031     });
5032 };
5033
5034 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5035     
5036     tag: 'div',
5037     cls: '',
5038     html: '',
5039     preventDefault: false, 
5040     clickable: false,
5041     
5042     getAutoCreate : function(){
5043         
5044         var cfg = {
5045             tag: this.tag,
5046             // cls: this.cls, double assign in parent class Component.js :: onRender
5047             html: this.html
5048         };
5049         
5050         return cfg;
5051     },
5052     
5053     initEvents: function() 
5054     {
5055         Roo.bootstrap.Element.superclass.initEvents.call(this);
5056         
5057         if(this.clickable){
5058             this.el.on('click', this.onClick, this);
5059         }
5060         
5061     },
5062     
5063     onClick : function(e)
5064     {
5065         if(this.preventDefault){
5066             e.preventDefault();
5067         }
5068         
5069         this.fireEvent('click', this, e);
5070     },
5071     
5072     getValue : function()
5073     {
5074         return this.el.dom.innerHTML;
5075     },
5076     
5077     setValue : function(value)
5078     {
5079         this.el.dom.innerHTML = value;
5080     }
5081    
5082 });
5083
5084  
5085
5086  /*
5087  * - LGPL
5088  *
5089  * pagination
5090  * 
5091  */
5092
5093 /**
5094  * @class Roo.bootstrap.Pagination
5095  * @extends Roo.bootstrap.Component
5096  * Bootstrap Pagination class
5097  * @cfg {String} size xs | sm | md | lg
5098  * @cfg {Boolean} inverse false | true
5099  * 
5100  * @constructor
5101  * Create a new Pagination
5102  * @param {Object} config The config object
5103  */
5104
5105 Roo.bootstrap.Pagination = function(config){
5106     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5107 };
5108
5109 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5110     
5111     cls: false,
5112     size: false,
5113     inverse: false,
5114     
5115     getAutoCreate : function(){
5116         var cfg = {
5117             tag: 'ul',
5118                 cls: 'pagination'
5119         };
5120         if (this.inverse) {
5121             cfg.cls += ' inverse';
5122         }
5123         if (this.html) {
5124             cfg.html=this.html;
5125         }
5126         if (this.cls) {
5127             cfg.cls += " " + this.cls;
5128         }
5129         return cfg;
5130     }
5131    
5132 });
5133
5134  
5135
5136  /*
5137  * - LGPL
5138  *
5139  * Pagination item
5140  * 
5141  */
5142
5143
5144 /**
5145  * @class Roo.bootstrap.PaginationItem
5146  * @extends Roo.bootstrap.Component
5147  * Bootstrap PaginationItem class
5148  * @cfg {String} html text
5149  * @cfg {String} href the link
5150  * @cfg {Boolean} preventDefault (true | false) default true
5151  * @cfg {Boolean} active (true | false) default false
5152  * @cfg {Boolean} disabled default false
5153  * 
5154  * 
5155  * @constructor
5156  * Create a new PaginationItem
5157  * @param {Object} config The config object
5158  */
5159
5160
5161 Roo.bootstrap.PaginationItem = function(config){
5162     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5163     this.addEvents({
5164         // raw events
5165         /**
5166          * @event click
5167          * The raw click event for the entire grid.
5168          * @param {Roo.EventObject} e
5169          */
5170         "click" : true
5171     });
5172 };
5173
5174 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5175     
5176     href : false,
5177     html : false,
5178     preventDefault: true,
5179     active : false,
5180     cls : false,
5181     disabled: false,
5182     
5183     getAutoCreate : function(){
5184         var cfg= {
5185             tag: 'li',
5186             cn: [
5187                 {
5188                     tag : 'a',
5189                     href : this.href ? this.href : '#',
5190                     html : this.html ? this.html : ''
5191                 }
5192             ]
5193         };
5194         
5195         if(this.cls){
5196             cfg.cls = this.cls;
5197         }
5198         
5199         if(this.disabled){
5200             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5201         }
5202         
5203         if(this.active){
5204             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5205         }
5206         
5207         return cfg;
5208     },
5209     
5210     initEvents: function() {
5211         
5212         this.el.on('click', this.onClick, this);
5213         
5214     },
5215     onClick : function(e)
5216     {
5217         Roo.log('PaginationItem on click ');
5218         if(this.preventDefault){
5219             e.preventDefault();
5220         }
5221         
5222         if(this.disabled){
5223             return;
5224         }
5225         
5226         this.fireEvent('click', this, e);
5227     }
5228    
5229 });
5230
5231  
5232
5233  /*
5234  * - LGPL
5235  *
5236  * slider
5237  * 
5238  */
5239
5240
5241 /**
5242  * @class Roo.bootstrap.Slider
5243  * @extends Roo.bootstrap.Component
5244  * Bootstrap Slider class
5245  *    
5246  * @constructor
5247  * Create a new Slider
5248  * @param {Object} config The config object
5249  */
5250
5251 Roo.bootstrap.Slider = function(config){
5252     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5253 };
5254
5255 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5256     
5257     getAutoCreate : function(){
5258         
5259         var cfg = {
5260             tag: 'div',
5261             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5262             cn: [
5263                 {
5264                     tag: 'a',
5265                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5266                 }
5267             ]
5268         };
5269         
5270         return cfg;
5271     }
5272    
5273 });
5274
5275  /*
5276  * Based on:
5277  * Ext JS Library 1.1.1
5278  * Copyright(c) 2006-2007, Ext JS, LLC.
5279  *
5280  * Originally Released Under LGPL - original licence link has changed is not relivant.
5281  *
5282  * Fork - LGPL
5283  * <script type="text/javascript">
5284  */
5285  
5286
5287 /**
5288  * @class Roo.grid.ColumnModel
5289  * @extends Roo.util.Observable
5290  * This is the default implementation of a ColumnModel used by the Grid. It defines
5291  * the columns in the grid.
5292  * <br>Usage:<br>
5293  <pre><code>
5294  var colModel = new Roo.grid.ColumnModel([
5295         {header: "Ticker", width: 60, sortable: true, locked: true},
5296         {header: "Company Name", width: 150, sortable: true},
5297         {header: "Market Cap.", width: 100, sortable: true},
5298         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5299         {header: "Employees", width: 100, sortable: true, resizable: false}
5300  ]);
5301  </code></pre>
5302  * <p>
5303  
5304  * The config options listed for this class are options which may appear in each
5305  * individual column definition.
5306  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5307  * @constructor
5308  * @param {Object} config An Array of column config objects. See this class's
5309  * config objects for details.
5310 */
5311 Roo.grid.ColumnModel = function(config){
5312         /**
5313      * The config passed into the constructor
5314      */
5315     this.config = config;
5316     this.lookup = {};
5317
5318     // if no id, create one
5319     // if the column does not have a dataIndex mapping,
5320     // map it to the order it is in the config
5321     for(var i = 0, len = config.length; i < len; i++){
5322         var c = config[i];
5323         if(typeof c.dataIndex == "undefined"){
5324             c.dataIndex = i;
5325         }
5326         if(typeof c.renderer == "string"){
5327             c.renderer = Roo.util.Format[c.renderer];
5328         }
5329         if(typeof c.id == "undefined"){
5330             c.id = Roo.id();
5331         }
5332         if(c.editor && c.editor.xtype){
5333             c.editor  = Roo.factory(c.editor, Roo.grid);
5334         }
5335         if(c.editor && c.editor.isFormField){
5336             c.editor = new Roo.grid.GridEditor(c.editor);
5337         }
5338         this.lookup[c.id] = c;
5339     }
5340
5341     /**
5342      * The width of columns which have no width specified (defaults to 100)
5343      * @type Number
5344      */
5345     this.defaultWidth = 100;
5346
5347     /**
5348      * Default sortable of columns which have no sortable specified (defaults to false)
5349      * @type Boolean
5350      */
5351     this.defaultSortable = false;
5352
5353     this.addEvents({
5354         /**
5355              * @event widthchange
5356              * Fires when the width of a column changes.
5357              * @param {ColumnModel} this
5358              * @param {Number} columnIndex The column index
5359              * @param {Number} newWidth The new width
5360              */
5361             "widthchange": true,
5362         /**
5363              * @event headerchange
5364              * Fires when the text of a header changes.
5365              * @param {ColumnModel} this
5366              * @param {Number} columnIndex The column index
5367              * @param {Number} newText The new header text
5368              */
5369             "headerchange": true,
5370         /**
5371              * @event hiddenchange
5372              * Fires when a column is hidden or "unhidden".
5373              * @param {ColumnModel} this
5374              * @param {Number} columnIndex The column index
5375              * @param {Boolean} hidden true if hidden, false otherwise
5376              */
5377             "hiddenchange": true,
5378             /**
5379          * @event columnmoved
5380          * Fires when a column is moved.
5381          * @param {ColumnModel} this
5382          * @param {Number} oldIndex
5383          * @param {Number} newIndex
5384          */
5385         "columnmoved" : true,
5386         /**
5387          * @event columlockchange
5388          * Fires when a column's locked state is changed
5389          * @param {ColumnModel} this
5390          * @param {Number} colIndex
5391          * @param {Boolean} locked true if locked
5392          */
5393         "columnlockchange" : true
5394     });
5395     Roo.grid.ColumnModel.superclass.constructor.call(this);
5396 };
5397 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5398     /**
5399      * @cfg {String} header The header text to display in the Grid view.
5400      */
5401     /**
5402      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5403      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5404      * specified, the column's index is used as an index into the Record's data Array.
5405      */
5406     /**
5407      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5408      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5409      */
5410     /**
5411      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5412      * Defaults to the value of the {@link #defaultSortable} property.
5413      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5414      */
5415     /**
5416      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5417      */
5418     /**
5419      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5420      */
5421     /**
5422      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5423      */
5424     /**
5425      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5426      */
5427     /**
5428      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5429      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5430      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5431      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5432      */
5433        /**
5434      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5435      */
5436     /**
5437      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5438      */
5439     /**
5440      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5441      */
5442     /**
5443      * @cfg {String} cursor (Optional)
5444      */
5445     /**
5446      * @cfg {String} tooltip (Optional)
5447      */
5448     /**
5449      * @cfg {Number} xs (Optional)
5450      */
5451     /**
5452      * @cfg {Number} sm (Optional)
5453      */
5454     /**
5455      * @cfg {Number} md (Optional)
5456      */
5457     /**
5458      * @cfg {Number} lg (Optional)
5459      */
5460     /**
5461      * Returns the id of the column at the specified index.
5462      * @param {Number} index The column index
5463      * @return {String} the id
5464      */
5465     getColumnId : function(index){
5466         return this.config[index].id;
5467     },
5468
5469     /**
5470      * Returns the column for a specified id.
5471      * @param {String} id The column id
5472      * @return {Object} the column
5473      */
5474     getColumnById : function(id){
5475         return this.lookup[id];
5476     },
5477
5478     
5479     /**
5480      * Returns the column for a specified dataIndex.
5481      * @param {String} dataIndex The column dataIndex
5482      * @return {Object|Boolean} the column or false if not found
5483      */
5484     getColumnByDataIndex: function(dataIndex){
5485         var index = this.findColumnIndex(dataIndex);
5486         return index > -1 ? this.config[index] : false;
5487     },
5488     
5489     /**
5490      * Returns the index for a specified column id.
5491      * @param {String} id The column id
5492      * @return {Number} the index, or -1 if not found
5493      */
5494     getIndexById : function(id){
5495         for(var i = 0, len = this.config.length; i < len; i++){
5496             if(this.config[i].id == id){
5497                 return i;
5498             }
5499         }
5500         return -1;
5501     },
5502     
5503     /**
5504      * Returns the index for a specified column dataIndex.
5505      * @param {String} dataIndex The column dataIndex
5506      * @return {Number} the index, or -1 if not found
5507      */
5508     
5509     findColumnIndex : function(dataIndex){
5510         for(var i = 0, len = this.config.length; i < len; i++){
5511             if(this.config[i].dataIndex == dataIndex){
5512                 return i;
5513             }
5514         }
5515         return -1;
5516     },
5517     
5518     
5519     moveColumn : function(oldIndex, newIndex){
5520         var c = this.config[oldIndex];
5521         this.config.splice(oldIndex, 1);
5522         this.config.splice(newIndex, 0, c);
5523         this.dataMap = null;
5524         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5525     },
5526
5527     isLocked : function(colIndex){
5528         return this.config[colIndex].locked === true;
5529     },
5530
5531     setLocked : function(colIndex, value, suppressEvent){
5532         if(this.isLocked(colIndex) == value){
5533             return;
5534         }
5535         this.config[colIndex].locked = value;
5536         if(!suppressEvent){
5537             this.fireEvent("columnlockchange", this, colIndex, value);
5538         }
5539     },
5540
5541     getTotalLockedWidth : function(){
5542         var totalWidth = 0;
5543         for(var i = 0; i < this.config.length; i++){
5544             if(this.isLocked(i) && !this.isHidden(i)){
5545                 this.totalWidth += this.getColumnWidth(i);
5546             }
5547         }
5548         return totalWidth;
5549     },
5550
5551     getLockedCount : function(){
5552         for(var i = 0, len = this.config.length; i < len; i++){
5553             if(!this.isLocked(i)){
5554                 return i;
5555             }
5556         }
5557         
5558         return this.config.length;
5559     },
5560
5561     /**
5562      * Returns the number of columns.
5563      * @return {Number}
5564      */
5565     getColumnCount : function(visibleOnly){
5566         if(visibleOnly === true){
5567             var c = 0;
5568             for(var i = 0, len = this.config.length; i < len; i++){
5569                 if(!this.isHidden(i)){
5570                     c++;
5571                 }
5572             }
5573             return c;
5574         }
5575         return this.config.length;
5576     },
5577
5578     /**
5579      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5580      * @param {Function} fn
5581      * @param {Object} scope (optional)
5582      * @return {Array} result
5583      */
5584     getColumnsBy : function(fn, scope){
5585         var r = [];
5586         for(var i = 0, len = this.config.length; i < len; i++){
5587             var c = this.config[i];
5588             if(fn.call(scope||this, c, i) === true){
5589                 r[r.length] = c;
5590             }
5591         }
5592         return r;
5593     },
5594
5595     /**
5596      * Returns true if the specified column is sortable.
5597      * @param {Number} col The column index
5598      * @return {Boolean}
5599      */
5600     isSortable : function(col){
5601         if(typeof this.config[col].sortable == "undefined"){
5602             return this.defaultSortable;
5603         }
5604         return this.config[col].sortable;
5605     },
5606
5607     /**
5608      * Returns the rendering (formatting) function defined for the column.
5609      * @param {Number} col The column index.
5610      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5611      */
5612     getRenderer : function(col){
5613         if(!this.config[col].renderer){
5614             return Roo.grid.ColumnModel.defaultRenderer;
5615         }
5616         return this.config[col].renderer;
5617     },
5618
5619     /**
5620      * Sets the rendering (formatting) function for a column.
5621      * @param {Number} col The column index
5622      * @param {Function} fn The function to use to process the cell's raw data
5623      * to return HTML markup for the grid view. The render function is called with
5624      * the following parameters:<ul>
5625      * <li>Data value.</li>
5626      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5627      * <li>css A CSS style string to apply to the table cell.</li>
5628      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5629      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5630      * <li>Row index</li>
5631      * <li>Column index</li>
5632      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5633      */
5634     setRenderer : function(col, fn){
5635         this.config[col].renderer = fn;
5636     },
5637
5638     /**
5639      * Returns the width for the specified column.
5640      * @param {Number} col The column index
5641      * @return {Number}
5642      */
5643     getColumnWidth : function(col){
5644         return this.config[col].width * 1 || this.defaultWidth;
5645     },
5646
5647     /**
5648      * Sets the width for a column.
5649      * @param {Number} col The column index
5650      * @param {Number} width The new width
5651      */
5652     setColumnWidth : function(col, width, suppressEvent){
5653         this.config[col].width = width;
5654         this.totalWidth = null;
5655         if(!suppressEvent){
5656              this.fireEvent("widthchange", this, col, width);
5657         }
5658     },
5659
5660     /**
5661      * Returns the total width of all columns.
5662      * @param {Boolean} includeHidden True to include hidden column widths
5663      * @return {Number}
5664      */
5665     getTotalWidth : function(includeHidden){
5666         if(!this.totalWidth){
5667             this.totalWidth = 0;
5668             for(var i = 0, len = this.config.length; i < len; i++){
5669                 if(includeHidden || !this.isHidden(i)){
5670                     this.totalWidth += this.getColumnWidth(i);
5671                 }
5672             }
5673         }
5674         return this.totalWidth;
5675     },
5676
5677     /**
5678      * Returns the header for the specified column.
5679      * @param {Number} col The column index
5680      * @return {String}
5681      */
5682     getColumnHeader : function(col){
5683         return this.config[col].header;
5684     },
5685
5686     /**
5687      * Sets the header for a column.
5688      * @param {Number} col The column index
5689      * @param {String} header The new header
5690      */
5691     setColumnHeader : function(col, header){
5692         this.config[col].header = header;
5693         this.fireEvent("headerchange", this, col, header);
5694     },
5695
5696     /**
5697      * Returns the tooltip for the specified column.
5698      * @param {Number} col The column index
5699      * @return {String}
5700      */
5701     getColumnTooltip : function(col){
5702             return this.config[col].tooltip;
5703     },
5704     /**
5705      * Sets the tooltip for a column.
5706      * @param {Number} col The column index
5707      * @param {String} tooltip The new tooltip
5708      */
5709     setColumnTooltip : function(col, tooltip){
5710             this.config[col].tooltip = tooltip;
5711     },
5712
5713     /**
5714      * Returns the dataIndex for the specified column.
5715      * @param {Number} col The column index
5716      * @return {Number}
5717      */
5718     getDataIndex : function(col){
5719         return this.config[col].dataIndex;
5720     },
5721
5722     /**
5723      * Sets the dataIndex for a column.
5724      * @param {Number} col The column index
5725      * @param {Number} dataIndex The new dataIndex
5726      */
5727     setDataIndex : function(col, dataIndex){
5728         this.config[col].dataIndex = dataIndex;
5729     },
5730
5731     
5732     
5733     /**
5734      * Returns true if the cell is editable.
5735      * @param {Number} colIndex The column index
5736      * @param {Number} rowIndex The row index - this is nto actually used..?
5737      * @return {Boolean}
5738      */
5739     isCellEditable : function(colIndex, rowIndex){
5740         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5741     },
5742
5743     /**
5744      * Returns the editor defined for the cell/column.
5745      * return false or null to disable editing.
5746      * @param {Number} colIndex The column index
5747      * @param {Number} rowIndex The row index
5748      * @return {Object}
5749      */
5750     getCellEditor : function(colIndex, rowIndex){
5751         return this.config[colIndex].editor;
5752     },
5753
5754     /**
5755      * Sets if a column is editable.
5756      * @param {Number} col The column index
5757      * @param {Boolean} editable True if the column is editable
5758      */
5759     setEditable : function(col, editable){
5760         this.config[col].editable = editable;
5761     },
5762
5763
5764     /**
5765      * Returns true if the column is hidden.
5766      * @param {Number} colIndex The column index
5767      * @return {Boolean}
5768      */
5769     isHidden : function(colIndex){
5770         return this.config[colIndex].hidden;
5771     },
5772
5773
5774     /**
5775      * Returns true if the column width cannot be changed
5776      */
5777     isFixed : function(colIndex){
5778         return this.config[colIndex].fixed;
5779     },
5780
5781     /**
5782      * Returns true if the column can be resized
5783      * @return {Boolean}
5784      */
5785     isResizable : function(colIndex){
5786         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5787     },
5788     /**
5789      * Sets if a column is hidden.
5790      * @param {Number} colIndex The column index
5791      * @param {Boolean} hidden True if the column is hidden
5792      */
5793     setHidden : function(colIndex, hidden){
5794         this.config[colIndex].hidden = hidden;
5795         this.totalWidth = null;
5796         this.fireEvent("hiddenchange", this, colIndex, hidden);
5797     },
5798
5799     /**
5800      * Sets the editor for a column.
5801      * @param {Number} col The column index
5802      * @param {Object} editor The editor object
5803      */
5804     setEditor : function(col, editor){
5805         this.config[col].editor = editor;
5806     }
5807 });
5808
5809 Roo.grid.ColumnModel.defaultRenderer = function(value)
5810 {
5811     if(typeof value == "object") {
5812         return value;
5813     }
5814         if(typeof value == "string" && value.length < 1){
5815             return "&#160;";
5816         }
5817     
5818         return String.format("{0}", value);
5819 };
5820
5821 // Alias for backwards compatibility
5822 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5823 /*
5824  * Based on:
5825  * Ext JS Library 1.1.1
5826  * Copyright(c) 2006-2007, Ext JS, LLC.
5827  *
5828  * Originally Released Under LGPL - original licence link has changed is not relivant.
5829  *
5830  * Fork - LGPL
5831  * <script type="text/javascript">
5832  */
5833  
5834 /**
5835  * @class Roo.LoadMask
5836  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5837  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5838  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5839  * element's UpdateManager load indicator and will be destroyed after the initial load.
5840  * @constructor
5841  * Create a new LoadMask
5842  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5843  * @param {Object} config The config object
5844  */
5845 Roo.LoadMask = function(el, config){
5846     this.el = Roo.get(el);
5847     Roo.apply(this, config);
5848     if(this.store){
5849         this.store.on('beforeload', this.onBeforeLoad, this);
5850         this.store.on('load', this.onLoad, this);
5851         this.store.on('loadexception', this.onLoadException, this);
5852         this.removeMask = false;
5853     }else{
5854         var um = this.el.getUpdateManager();
5855         um.showLoadIndicator = false; // disable the default indicator
5856         um.on('beforeupdate', this.onBeforeLoad, this);
5857         um.on('update', this.onLoad, this);
5858         um.on('failure', this.onLoad, this);
5859         this.removeMask = true;
5860     }
5861 };
5862
5863 Roo.LoadMask.prototype = {
5864     /**
5865      * @cfg {Boolean} removeMask
5866      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5867      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5868      */
5869     /**
5870      * @cfg {String} msg
5871      * The text to display in a centered loading message box (defaults to 'Loading...')
5872      */
5873     msg : 'Loading...',
5874     /**
5875      * @cfg {String} msgCls
5876      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5877      */
5878     msgCls : 'x-mask-loading',
5879
5880     /**
5881      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5882      * @type Boolean
5883      */
5884     disabled: false,
5885
5886     /**
5887      * Disables the mask to prevent it from being displayed
5888      */
5889     disable : function(){
5890        this.disabled = true;
5891     },
5892
5893     /**
5894      * Enables the mask so that it can be displayed
5895      */
5896     enable : function(){
5897         this.disabled = false;
5898     },
5899     
5900     onLoadException : function()
5901     {
5902         Roo.log(arguments);
5903         
5904         if (typeof(arguments[3]) != 'undefined') {
5905             Roo.MessageBox.alert("Error loading",arguments[3]);
5906         } 
5907         /*
5908         try {
5909             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5910                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5911             }   
5912         } catch(e) {
5913             
5914         }
5915         */
5916     
5917         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5918     },
5919     // private
5920     onLoad : function()
5921     {
5922         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5923     },
5924
5925     // private
5926     onBeforeLoad : function(){
5927         if(!this.disabled){
5928             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5929         }
5930     },
5931
5932     // private
5933     destroy : function(){
5934         if(this.store){
5935             this.store.un('beforeload', this.onBeforeLoad, this);
5936             this.store.un('load', this.onLoad, this);
5937             this.store.un('loadexception', this.onLoadException, this);
5938         }else{
5939             var um = this.el.getUpdateManager();
5940             um.un('beforeupdate', this.onBeforeLoad, this);
5941             um.un('update', this.onLoad, this);
5942             um.un('failure', this.onLoad, this);
5943         }
5944     }
5945 };/*
5946  * - LGPL
5947  *
5948  * table
5949  * 
5950  */
5951
5952 /**
5953  * @class Roo.bootstrap.Table
5954  * @extends Roo.bootstrap.Component
5955  * Bootstrap Table class
5956  * @cfg {String} cls table class
5957  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5958  * @cfg {String} bgcolor Specifies the background color for a table
5959  * @cfg {Number} border Specifies whether the table cells should have borders or not
5960  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5961  * @cfg {Number} cellspacing Specifies the space between cells
5962  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5963  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5964  * @cfg {String} sortable Specifies that the table should be sortable
5965  * @cfg {String} summary Specifies a summary of the content of a table
5966  * @cfg {Number} width Specifies the width of a table
5967  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5968  * 
5969  * @cfg {boolean} striped Should the rows be alternative striped
5970  * @cfg {boolean} bordered Add borders to the table
5971  * @cfg {boolean} hover Add hover highlighting
5972  * @cfg {boolean} condensed Format condensed
5973  * @cfg {boolean} responsive Format condensed
5974  * @cfg {Boolean} loadMask (true|false) default false
5975  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5976  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5977  * @cfg {Boolean} rowSelection (true|false) default false
5978  * @cfg {Boolean} cellSelection (true|false) default false
5979  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5980  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5981  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5982  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5983  
5984  * 
5985  * @constructor
5986  * Create a new Table
5987  * @param {Object} config The config object
5988  */
5989
5990 Roo.bootstrap.Table = function(config){
5991     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5992     
5993   
5994     
5995     // BC...
5996     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5997     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5998     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5999     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6000     
6001     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6002     if (this.sm) {
6003         this.sm.grid = this;
6004         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6005         this.sm = this.selModel;
6006         this.sm.xmodule = this.xmodule || false;
6007     }
6008     
6009     if (this.cm && typeof(this.cm.config) == 'undefined') {
6010         this.colModel = new Roo.grid.ColumnModel(this.cm);
6011         this.cm = this.colModel;
6012         this.cm.xmodule = this.xmodule || false;
6013     }
6014     if (this.store) {
6015         this.store= Roo.factory(this.store, Roo.data);
6016         this.ds = this.store;
6017         this.ds.xmodule = this.xmodule || false;
6018          
6019     }
6020     if (this.footer && this.store) {
6021         this.footer.dataSource = this.ds;
6022         this.footer = Roo.factory(this.footer);
6023     }
6024     
6025     /** @private */
6026     this.addEvents({
6027         /**
6028          * @event cellclick
6029          * Fires when a cell is clicked
6030          * @param {Roo.bootstrap.Table} this
6031          * @param {Roo.Element} el
6032          * @param {Number} rowIndex
6033          * @param {Number} columnIndex
6034          * @param {Roo.EventObject} e
6035          */
6036         "cellclick" : true,
6037         /**
6038          * @event celldblclick
6039          * Fires when a cell is double clicked
6040          * @param {Roo.bootstrap.Table} this
6041          * @param {Roo.Element} el
6042          * @param {Number} rowIndex
6043          * @param {Number} columnIndex
6044          * @param {Roo.EventObject} e
6045          */
6046         "celldblclick" : true,
6047         /**
6048          * @event rowclick
6049          * Fires when a row is clicked
6050          * @param {Roo.bootstrap.Table} this
6051          * @param {Roo.Element} el
6052          * @param {Number} rowIndex
6053          * @param {Roo.EventObject} e
6054          */
6055         "rowclick" : true,
6056         /**
6057          * @event rowdblclick
6058          * Fires when a row is double clicked
6059          * @param {Roo.bootstrap.Table} this
6060          * @param {Roo.Element} el
6061          * @param {Number} rowIndex
6062          * @param {Roo.EventObject} e
6063          */
6064         "rowdblclick" : true,
6065         /**
6066          * @event mouseover
6067          * Fires when a mouseover occur
6068          * @param {Roo.bootstrap.Table} this
6069          * @param {Roo.Element} el
6070          * @param {Number} rowIndex
6071          * @param {Number} columnIndex
6072          * @param {Roo.EventObject} e
6073          */
6074         "mouseover" : true,
6075         /**
6076          * @event mouseout
6077          * Fires when a mouseout occur
6078          * @param {Roo.bootstrap.Table} this
6079          * @param {Roo.Element} el
6080          * @param {Number} rowIndex
6081          * @param {Number} columnIndex
6082          * @param {Roo.EventObject} e
6083          */
6084         "mouseout" : true,
6085         /**
6086          * @event rowclass
6087          * Fires when a row is rendered, so you can change add a style to it.
6088          * @param {Roo.bootstrap.Table} this
6089          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6090          */
6091         'rowclass' : true,
6092           /**
6093          * @event rowsrendered
6094          * Fires when all the  rows have been rendered
6095          * @param {Roo.bootstrap.Table} this
6096          */
6097         'rowsrendered' : true,
6098         /**
6099          * @event contextmenu
6100          * The raw contextmenu event for the entire grid.
6101          * @param {Roo.EventObject} e
6102          */
6103         "contextmenu" : true,
6104         /**
6105          * @event rowcontextmenu
6106          * Fires when a row is right clicked
6107          * @param {Roo.bootstrap.Table} this
6108          * @param {Number} rowIndex
6109          * @param {Roo.EventObject} e
6110          */
6111         "rowcontextmenu" : true,
6112         /**
6113          * @event cellcontextmenu
6114          * Fires when a cell is right clicked
6115          * @param {Roo.bootstrap.Table} this
6116          * @param {Number} rowIndex
6117          * @param {Number} cellIndex
6118          * @param {Roo.EventObject} e
6119          */
6120          "cellcontextmenu" : true,
6121          /**
6122          * @event headercontextmenu
6123          * Fires when a header is right clicked
6124          * @param {Roo.bootstrap.Table} this
6125          * @param {Number} columnIndex
6126          * @param {Roo.EventObject} e
6127          */
6128         "headercontextmenu" : true
6129     });
6130 };
6131
6132 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6133     
6134     cls: false,
6135     align: false,
6136     bgcolor: false,
6137     border: false,
6138     cellpadding: false,
6139     cellspacing: false,
6140     frame: false,
6141     rules: false,
6142     sortable: false,
6143     summary: false,
6144     width: false,
6145     striped : false,
6146     scrollBody : false,
6147     bordered: false,
6148     hover:  false,
6149     condensed : false,
6150     responsive : false,
6151     sm : false,
6152     cm : false,
6153     store : false,
6154     loadMask : false,
6155     footerShow : true,
6156     headerShow : true,
6157   
6158     rowSelection : false,
6159     cellSelection : false,
6160     layout : false,
6161     
6162     // Roo.Element - the tbody
6163     mainBody: false,
6164     // Roo.Element - thead element
6165     mainHead: false,
6166     
6167     container: false, // used by gridpanel...
6168     
6169     lazyLoad : false,
6170     
6171     CSS : Roo.util.CSS,
6172     
6173     auto_hide_footer : false,
6174     
6175     getAutoCreate : function()
6176     {
6177         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6178         
6179         cfg = {
6180             tag: 'table',
6181             cls : 'table',
6182             cn : []
6183         };
6184         if (this.scrollBody) {
6185             cfg.cls += ' table-body-fixed';
6186         }    
6187         if (this.striped) {
6188             cfg.cls += ' table-striped';
6189         }
6190         
6191         if (this.hover) {
6192             cfg.cls += ' table-hover';
6193         }
6194         if (this.bordered) {
6195             cfg.cls += ' table-bordered';
6196         }
6197         if (this.condensed) {
6198             cfg.cls += ' table-condensed';
6199         }
6200         if (this.responsive) {
6201             cfg.cls += ' table-responsive';
6202         }
6203         
6204         if (this.cls) {
6205             cfg.cls+=  ' ' +this.cls;
6206         }
6207         
6208         // this lot should be simplifed...
6209         var _t = this;
6210         var cp = [
6211             'align',
6212             'bgcolor',
6213             'border',
6214             'cellpadding',
6215             'cellspacing',
6216             'frame',
6217             'rules',
6218             'sortable',
6219             'summary',
6220             'width'
6221         ].forEach(function(k) {
6222             if (_t[k]) {
6223                 cfg[k] = _t[k];
6224             }
6225         });
6226         
6227         
6228         if (this.layout) {
6229             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6230         }
6231         
6232         if(this.store || this.cm){
6233             if(this.headerShow){
6234                 cfg.cn.push(this.renderHeader());
6235             }
6236             
6237             cfg.cn.push(this.renderBody());
6238             
6239             if(this.footerShow){
6240                 cfg.cn.push(this.renderFooter());
6241             }
6242             // where does this come from?
6243             //cfg.cls+=  ' TableGrid';
6244         }
6245         
6246         return { cn : [ cfg ] };
6247     },
6248     
6249     initEvents : function()
6250     {   
6251         if(!this.store || !this.cm){
6252             return;
6253         }
6254         if (this.selModel) {
6255             this.selModel.initEvents();
6256         }
6257         
6258         
6259         //Roo.log('initEvents with ds!!!!');
6260         
6261         this.mainBody = this.el.select('tbody', true).first();
6262         this.mainHead = this.el.select('thead', true).first();
6263         this.mainFoot = this.el.select('tfoot', true).first();
6264         
6265         
6266         
6267         var _this = this;
6268         
6269         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6270             e.on('click', _this.sort, _this);
6271         });
6272         
6273         this.mainBody.on("click", this.onClick, this);
6274         this.mainBody.on("dblclick", this.onDblClick, this);
6275         
6276         // why is this done????? = it breaks dialogs??
6277         //this.parent().el.setStyle('position', 'relative');
6278         
6279         
6280         if (this.footer) {
6281             this.footer.parentId = this.id;
6282             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6283             
6284             if(this.lazyLoad){
6285                 this.el.select('tfoot tr td').first().addClass('hide');
6286             }
6287         } 
6288         
6289         if(this.loadMask) {
6290             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6291         }
6292         
6293         this.store.on('load', this.onLoad, this);
6294         this.store.on('beforeload', this.onBeforeLoad, this);
6295         this.store.on('update', this.onUpdate, this);
6296         this.store.on('add', this.onAdd, this);
6297         this.store.on("clear", this.clear, this);
6298         
6299         this.el.on("contextmenu", this.onContextMenu, this);
6300         
6301         this.mainBody.on('scroll', this.onBodyScroll, this);
6302         
6303         this.cm.on("headerchange", this.onHeaderChange, this);
6304         
6305         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6306         
6307     },
6308     
6309     onContextMenu : function(e, t)
6310     {
6311         this.processEvent("contextmenu", e);
6312     },
6313     
6314     processEvent : function(name, e)
6315     {
6316         if (name != 'touchstart' ) {
6317             this.fireEvent(name, e);    
6318         }
6319         
6320         var t = e.getTarget();
6321         
6322         var cell = Roo.get(t);
6323         
6324         if(!cell){
6325             return;
6326         }
6327         
6328         if(cell.findParent('tfoot', false, true)){
6329             return;
6330         }
6331         
6332         if(cell.findParent('thead', false, true)){
6333             
6334             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6335                 cell = Roo.get(t).findParent('th', false, true);
6336                 if (!cell) {
6337                     Roo.log("failed to find th in thead?");
6338                     Roo.log(e.getTarget());
6339                     return;
6340                 }
6341             }
6342             
6343             var cellIndex = cell.dom.cellIndex;
6344             
6345             var ename = name == 'touchstart' ? 'click' : name;
6346             this.fireEvent("header" + ename, this, cellIndex, e);
6347             
6348             return;
6349         }
6350         
6351         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6352             cell = Roo.get(t).findParent('td', false, true);
6353             if (!cell) {
6354                 Roo.log("failed to find th in tbody?");
6355                 Roo.log(e.getTarget());
6356                 return;
6357             }
6358         }
6359         
6360         var row = cell.findParent('tr', false, true);
6361         var cellIndex = cell.dom.cellIndex;
6362         var rowIndex = row.dom.rowIndex - 1;
6363         
6364         if(row !== false){
6365             
6366             this.fireEvent("row" + name, this, rowIndex, e);
6367             
6368             if(cell !== false){
6369             
6370                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6371             }
6372         }
6373         
6374     },
6375     
6376     onMouseover : function(e, el)
6377     {
6378         var cell = Roo.get(el);
6379         
6380         if(!cell){
6381             return;
6382         }
6383         
6384         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6385             cell = cell.findParent('td', false, true);
6386         }
6387         
6388         var row = cell.findParent('tr', false, true);
6389         var cellIndex = cell.dom.cellIndex;
6390         var rowIndex = row.dom.rowIndex - 1; // start from 0
6391         
6392         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6393         
6394     },
6395     
6396     onMouseout : function(e, el)
6397     {
6398         var cell = Roo.get(el);
6399         
6400         if(!cell){
6401             return;
6402         }
6403         
6404         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6405             cell = cell.findParent('td', false, true);
6406         }
6407         
6408         var row = cell.findParent('tr', false, true);
6409         var cellIndex = cell.dom.cellIndex;
6410         var rowIndex = row.dom.rowIndex - 1; // start from 0
6411         
6412         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6413         
6414     },
6415     
6416     onClick : function(e, el)
6417     {
6418         var cell = Roo.get(el);
6419         
6420         if(!cell || (!this.cellSelection && !this.rowSelection)){
6421             return;
6422         }
6423         
6424         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6425             cell = cell.findParent('td', false, true);
6426         }
6427         
6428         if(!cell || typeof(cell) == 'undefined'){
6429             return;
6430         }
6431         
6432         var row = cell.findParent('tr', false, true);
6433         
6434         if(!row || typeof(row) == 'undefined'){
6435             return;
6436         }
6437         
6438         var cellIndex = cell.dom.cellIndex;
6439         var rowIndex = this.getRowIndex(row);
6440         
6441         // why??? - should these not be based on SelectionModel?
6442         if(this.cellSelection){
6443             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6444         }
6445         
6446         if(this.rowSelection){
6447             this.fireEvent('rowclick', this, row, rowIndex, e);
6448         }
6449         
6450         
6451     },
6452         
6453     onDblClick : function(e,el)
6454     {
6455         var cell = Roo.get(el);
6456         
6457         if(!cell || (!this.cellSelection && !this.rowSelection)){
6458             return;
6459         }
6460         
6461         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6462             cell = cell.findParent('td', false, true);
6463         }
6464         
6465         if(!cell || typeof(cell) == 'undefined'){
6466             return;
6467         }
6468         
6469         var row = cell.findParent('tr', false, true);
6470         
6471         if(!row || typeof(row) == 'undefined'){
6472             return;
6473         }
6474         
6475         var cellIndex = cell.dom.cellIndex;
6476         var rowIndex = this.getRowIndex(row);
6477         
6478         if(this.cellSelection){
6479             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6480         }
6481         
6482         if(this.rowSelection){
6483             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6484         }
6485     },
6486     
6487     sort : function(e,el)
6488     {
6489         var col = Roo.get(el);
6490         
6491         if(!col.hasClass('sortable')){
6492             return;
6493         }
6494         
6495         var sort = col.attr('sort');
6496         var dir = 'ASC';
6497         
6498         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6499             dir = 'DESC';
6500         }
6501         
6502         this.store.sortInfo = {field : sort, direction : dir};
6503         
6504         if (this.footer) {
6505             Roo.log("calling footer first");
6506             this.footer.onClick('first');
6507         } else {
6508         
6509             this.store.load({ params : { start : 0 } });
6510         }
6511     },
6512     
6513     renderHeader : function()
6514     {
6515         var header = {
6516             tag: 'thead',
6517             cn : []
6518         };
6519         
6520         var cm = this.cm;
6521         this.totalWidth = 0;
6522         
6523         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6524             
6525             var config = cm.config[i];
6526             
6527             var c = {
6528                 tag: 'th',
6529                 cls : 'x-hcol-' + i,
6530                 style : '',
6531                 html: cm.getColumnHeader(i)
6532             };
6533             
6534             var hh = '';
6535             
6536             if(typeof(config.sortable) != 'undefined' && config.sortable){
6537                 c.cls = 'sortable';
6538                 c.html = '<i class="glyphicon"></i>' + c.html;
6539             }
6540             
6541             if(typeof(config.lgHeader) != 'undefined'){
6542                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6543             }
6544             
6545             if(typeof(config.mdHeader) != 'undefined'){
6546                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6547             }
6548             
6549             if(typeof(config.smHeader) != 'undefined'){
6550                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6551             }
6552             
6553             if(typeof(config.xsHeader) != 'undefined'){
6554                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6555             }
6556             
6557             if(hh.length){
6558                 c.html = hh;
6559             }
6560             
6561             if(typeof(config.tooltip) != 'undefined'){
6562                 c.tooltip = config.tooltip;
6563             }
6564             
6565             if(typeof(config.colspan) != 'undefined'){
6566                 c.colspan = config.colspan;
6567             }
6568             
6569             if(typeof(config.hidden) != 'undefined' && config.hidden){
6570                 c.style += ' display:none;';
6571             }
6572             
6573             if(typeof(config.dataIndex) != 'undefined'){
6574                 c.sort = config.dataIndex;
6575             }
6576             
6577            
6578             
6579             if(typeof(config.align) != 'undefined' && config.align.length){
6580                 c.style += ' text-align:' + config.align + ';';
6581             }
6582             
6583             if(typeof(config.width) != 'undefined'){
6584                 c.style += ' width:' + config.width + 'px;';
6585                 this.totalWidth += config.width;
6586             } else {
6587                 this.totalWidth += 100; // assume minimum of 100 per column?
6588             }
6589             
6590             if(typeof(config.cls) != 'undefined'){
6591                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6592             }
6593             
6594             ['xs','sm','md','lg'].map(function(size){
6595                 
6596                 if(typeof(config[size]) == 'undefined'){
6597                     return;
6598                 }
6599                 
6600                 if (!config[size]) { // 0 = hidden
6601                     c.cls += ' hidden-' + size;
6602                     return;
6603                 }
6604                 
6605                 c.cls += ' col-' + size + '-' + config[size];
6606
6607             });
6608             
6609             header.cn.push(c)
6610         }
6611         
6612         return header;
6613     },
6614     
6615     renderBody : function()
6616     {
6617         var body = {
6618             tag: 'tbody',
6619             cn : [
6620                 {
6621                     tag: 'tr',
6622                     cn : [
6623                         {
6624                             tag : 'td',
6625                             colspan :  this.cm.getColumnCount()
6626                         }
6627                     ]
6628                 }
6629             ]
6630         };
6631         
6632         return body;
6633     },
6634     
6635     renderFooter : function()
6636     {
6637         var footer = {
6638             tag: 'tfoot',
6639             cn : [
6640                 {
6641                     tag: 'tr',
6642                     cn : [
6643                         {
6644                             tag : 'td',
6645                             colspan :  this.cm.getColumnCount()
6646                         }
6647                     ]
6648                 }
6649             ]
6650         };
6651         
6652         return footer;
6653     },
6654     
6655     
6656     
6657     onLoad : function()
6658     {
6659 //        Roo.log('ds onload');
6660         this.clear();
6661         
6662         var _this = this;
6663         var cm = this.cm;
6664         var ds = this.store;
6665         
6666         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6667             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6668             if (_this.store.sortInfo) {
6669                     
6670                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6671                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6672                 }
6673                 
6674                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6675                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6676                 }
6677             }
6678         });
6679         
6680         var tbody =  this.mainBody;
6681               
6682         if(ds.getCount() > 0){
6683             ds.data.each(function(d,rowIndex){
6684                 var row =  this.renderRow(cm, ds, rowIndex);
6685                 
6686                 tbody.createChild(row);
6687                 
6688                 var _this = this;
6689                 
6690                 if(row.cellObjects.length){
6691                     Roo.each(row.cellObjects, function(r){
6692                         _this.renderCellObject(r);
6693                     })
6694                 }
6695                 
6696             }, this);
6697         }
6698         
6699         var tfoot = this.el.select('tfoot', true).first();
6700         
6701         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6702             
6703             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6704             
6705             var total = this.ds.getTotalCount();
6706             
6707             if(this.footer.pageSize < total){
6708                 this.mainFoot.show();
6709             }
6710         }
6711         
6712         Roo.each(this.el.select('tbody td', true).elements, function(e){
6713             e.on('mouseover', _this.onMouseover, _this);
6714         });
6715         
6716         Roo.each(this.el.select('tbody td', true).elements, function(e){
6717             e.on('mouseout', _this.onMouseout, _this);
6718         });
6719         this.fireEvent('rowsrendered', this);
6720         
6721         this.autoSize();
6722     },
6723     
6724     
6725     onUpdate : function(ds,record)
6726     {
6727         this.refreshRow(record);
6728         this.autoSize();
6729     },
6730     
6731     onRemove : function(ds, record, index, isUpdate){
6732         if(isUpdate !== true){
6733             this.fireEvent("beforerowremoved", this, index, record);
6734         }
6735         var bt = this.mainBody.dom;
6736         
6737         var rows = this.el.select('tbody > tr', true).elements;
6738         
6739         if(typeof(rows[index]) != 'undefined'){
6740             bt.removeChild(rows[index].dom);
6741         }
6742         
6743 //        if(bt.rows[index]){
6744 //            bt.removeChild(bt.rows[index]);
6745 //        }
6746         
6747         if(isUpdate !== true){
6748             //this.stripeRows(index);
6749             //this.syncRowHeights(index, index);
6750             //this.layout();
6751             this.fireEvent("rowremoved", this, index, record);
6752         }
6753     },
6754     
6755     onAdd : function(ds, records, rowIndex)
6756     {
6757         //Roo.log('on Add called');
6758         // - note this does not handle multiple adding very well..
6759         var bt = this.mainBody.dom;
6760         for (var i =0 ; i < records.length;i++) {
6761             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6762             //Roo.log(records[i]);
6763             //Roo.log(this.store.getAt(rowIndex+i));
6764             this.insertRow(this.store, rowIndex + i, false);
6765             return;
6766         }
6767         
6768     },
6769     
6770     
6771     refreshRow : function(record){
6772         var ds = this.store, index;
6773         if(typeof record == 'number'){
6774             index = record;
6775             record = ds.getAt(index);
6776         }else{
6777             index = ds.indexOf(record);
6778         }
6779         this.insertRow(ds, index, true);
6780         this.autoSize();
6781         this.onRemove(ds, record, index+1, true);
6782         this.autoSize();
6783         //this.syncRowHeights(index, index);
6784         //this.layout();
6785         this.fireEvent("rowupdated", this, index, record);
6786     },
6787     
6788     insertRow : function(dm, rowIndex, isUpdate){
6789         
6790         if(!isUpdate){
6791             this.fireEvent("beforerowsinserted", this, rowIndex);
6792         }
6793             //var s = this.getScrollState();
6794         var row = this.renderRow(this.cm, this.store, rowIndex);
6795         // insert before rowIndex..
6796         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6797         
6798         var _this = this;
6799                 
6800         if(row.cellObjects.length){
6801             Roo.each(row.cellObjects, function(r){
6802                 _this.renderCellObject(r);
6803             })
6804         }
6805             
6806         if(!isUpdate){
6807             this.fireEvent("rowsinserted", this, rowIndex);
6808             //this.syncRowHeights(firstRow, lastRow);
6809             //this.stripeRows(firstRow);
6810             //this.layout();
6811         }
6812         
6813     },
6814     
6815     
6816     getRowDom : function(rowIndex)
6817     {
6818         var rows = this.el.select('tbody > tr', true).elements;
6819         
6820         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6821         
6822     },
6823     // returns the object tree for a tr..
6824   
6825     
6826     renderRow : function(cm, ds, rowIndex) 
6827     {
6828         var d = ds.getAt(rowIndex);
6829         
6830         var row = {
6831             tag : 'tr',
6832             cls : 'x-row-' + rowIndex,
6833             cn : []
6834         };
6835             
6836         var cellObjects = [];
6837         
6838         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6839             var config = cm.config[i];
6840             
6841             var renderer = cm.getRenderer(i);
6842             var value = '';
6843             var id = false;
6844             
6845             if(typeof(renderer) !== 'undefined'){
6846                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6847             }
6848             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6849             // and are rendered into the cells after the row is rendered - using the id for the element.
6850             
6851             if(typeof(value) === 'object'){
6852                 id = Roo.id();
6853                 cellObjects.push({
6854                     container : id,
6855                     cfg : value 
6856                 })
6857             }
6858             
6859             var rowcfg = {
6860                 record: d,
6861                 rowIndex : rowIndex,
6862                 colIndex : i,
6863                 rowClass : ''
6864             };
6865
6866             this.fireEvent('rowclass', this, rowcfg);
6867             
6868             var td = {
6869                 tag: 'td',
6870                 cls : rowcfg.rowClass + ' x-col-' + i,
6871                 style: '',
6872                 html: (typeof(value) === 'object') ? '' : value
6873             };
6874             
6875             if (id) {
6876                 td.id = id;
6877             }
6878             
6879             if(typeof(config.colspan) != 'undefined'){
6880                 td.colspan = config.colspan;
6881             }
6882             
6883             if(typeof(config.hidden) != 'undefined' && config.hidden){
6884                 td.style += ' display:none;';
6885             }
6886             
6887             if(typeof(config.align) != 'undefined' && config.align.length){
6888                 td.style += ' text-align:' + config.align + ';';
6889             }
6890             if(typeof(config.valign) != 'undefined' && config.valign.length){
6891                 td.style += ' vertical-align:' + config.valign + ';';
6892             }
6893             
6894             if(typeof(config.width) != 'undefined'){
6895                 td.style += ' width:' +  config.width + 'px;';
6896             }
6897             
6898             if(typeof(config.cursor) != 'undefined'){
6899                 td.style += ' cursor:' +  config.cursor + ';';
6900             }
6901             
6902             if(typeof(config.cls) != 'undefined'){
6903                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6904             }
6905             
6906             ['xs','sm','md','lg'].map(function(size){
6907                 
6908                 if(typeof(config[size]) == 'undefined'){
6909                     return;
6910                 }
6911                 
6912                 if (!config[size]) { // 0 = hidden
6913                     td.cls += ' hidden-' + size;
6914                     return;
6915                 }
6916                 
6917                 td.cls += ' col-' + size + '-' + config[size];
6918
6919             });
6920             
6921             row.cn.push(td);
6922            
6923         }
6924         
6925         row.cellObjects = cellObjects;
6926         
6927         return row;
6928           
6929     },
6930     
6931     
6932     
6933     onBeforeLoad : function()
6934     {
6935         
6936     },
6937      /**
6938      * Remove all rows
6939      */
6940     clear : function()
6941     {
6942         this.el.select('tbody', true).first().dom.innerHTML = '';
6943     },
6944     /**
6945      * Show or hide a row.
6946      * @param {Number} rowIndex to show or hide
6947      * @param {Boolean} state hide
6948      */
6949     setRowVisibility : function(rowIndex, state)
6950     {
6951         var bt = this.mainBody.dom;
6952         
6953         var rows = this.el.select('tbody > tr', true).elements;
6954         
6955         if(typeof(rows[rowIndex]) == 'undefined'){
6956             return;
6957         }
6958         rows[rowIndex].dom.style.display = state ? '' : 'none';
6959     },
6960     
6961     
6962     getSelectionModel : function(){
6963         if(!this.selModel){
6964             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6965         }
6966         return this.selModel;
6967     },
6968     /*
6969      * Render the Roo.bootstrap object from renderder
6970      */
6971     renderCellObject : function(r)
6972     {
6973         var _this = this;
6974         
6975         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6976         
6977         var t = r.cfg.render(r.container);
6978         
6979         if(r.cfg.cn){
6980             Roo.each(r.cfg.cn, function(c){
6981                 var child = {
6982                     container: t.getChildContainer(),
6983                     cfg: c
6984                 };
6985                 _this.renderCellObject(child);
6986             })
6987         }
6988     },
6989     
6990     getRowIndex : function(row)
6991     {
6992         var rowIndex = -1;
6993         
6994         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6995             if(el != row){
6996                 return;
6997             }
6998             
6999             rowIndex = index;
7000         });
7001         
7002         return rowIndex;
7003     },
7004      /**
7005      * Returns the grid's underlying element = used by panel.Grid
7006      * @return {Element} The element
7007      */
7008     getGridEl : function(){
7009         return this.el;
7010     },
7011      /**
7012      * Forces a resize - used by panel.Grid
7013      * @return {Element} The element
7014      */
7015     autoSize : function()
7016     {
7017         //var ctr = Roo.get(this.container.dom.parentElement);
7018         var ctr = Roo.get(this.el.dom);
7019         
7020         var thd = this.getGridEl().select('thead',true).first();
7021         var tbd = this.getGridEl().select('tbody', true).first();
7022         var tfd = this.getGridEl().select('tfoot', true).first();
7023         
7024         var cw = ctr.getWidth();
7025         
7026         if (tbd) {
7027             
7028             tbd.setSize(ctr.getWidth(),
7029                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7030             );
7031             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7032             cw -= barsize;
7033         }
7034         cw = Math.max(cw, this.totalWidth);
7035         this.getGridEl().select('tr',true).setWidth(cw);
7036         // resize 'expandable coloumn?
7037         
7038         return; // we doe not have a view in this design..
7039         
7040     },
7041     onBodyScroll: function()
7042     {
7043         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7044         if(this.mainHead){
7045             this.mainHead.setStyle({
7046                 'position' : 'relative',
7047                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7048             });
7049         }
7050         
7051         if(this.lazyLoad){
7052             
7053             var scrollHeight = this.mainBody.dom.scrollHeight;
7054             
7055             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7056             
7057             var height = this.mainBody.getHeight();
7058             
7059             if(scrollHeight - height == scrollTop) {
7060                 
7061                 var total = this.ds.getTotalCount();
7062                 
7063                 if(this.footer.cursor + this.footer.pageSize < total){
7064                     
7065                     this.footer.ds.load({
7066                         params : {
7067                             start : this.footer.cursor + this.footer.pageSize,
7068                             limit : this.footer.pageSize
7069                         },
7070                         add : true
7071                     });
7072                 }
7073             }
7074             
7075         }
7076     },
7077     
7078     onHeaderChange : function()
7079     {
7080         var header = this.renderHeader();
7081         var table = this.el.select('table', true).first();
7082         
7083         this.mainHead.remove();
7084         this.mainHead = table.createChild(header, this.mainBody, false);
7085     },
7086     
7087     onHiddenChange : function(colModel, colIndex, hidden)
7088     {
7089         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7090         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7091         
7092         this.CSS.updateRule(thSelector, "display", "");
7093         this.CSS.updateRule(tdSelector, "display", "");
7094         
7095         if(hidden){
7096             this.CSS.updateRule(thSelector, "display", "none");
7097             this.CSS.updateRule(tdSelector, "display", "none");
7098         }
7099         
7100         this.onHeaderChange();
7101         this.onLoad();
7102         
7103     }
7104     
7105 });
7106
7107  
7108
7109  /*
7110  * - LGPL
7111  *
7112  * table cell
7113  * 
7114  */
7115
7116 /**
7117  * @class Roo.bootstrap.TableCell
7118  * @extends Roo.bootstrap.Component
7119  * Bootstrap TableCell class
7120  * @cfg {String} html cell contain text
7121  * @cfg {String} cls cell class
7122  * @cfg {String} tag cell tag (td|th) default td
7123  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7124  * @cfg {String} align Aligns the content in a cell
7125  * @cfg {String} axis Categorizes cells
7126  * @cfg {String} bgcolor Specifies the background color of a cell
7127  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7128  * @cfg {Number} colspan Specifies the number of columns a cell should span
7129  * @cfg {String} headers Specifies one or more header cells a cell is related to
7130  * @cfg {Number} height Sets the height of a cell
7131  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7132  * @cfg {Number} rowspan Sets the number of rows a cell should span
7133  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7134  * @cfg {String} valign Vertical aligns the content in a cell
7135  * @cfg {Number} width Specifies the width of a cell
7136  * 
7137  * @constructor
7138  * Create a new TableCell
7139  * @param {Object} config The config object
7140  */
7141
7142 Roo.bootstrap.TableCell = function(config){
7143     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7144 };
7145
7146 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7147     
7148     html: false,
7149     cls: false,
7150     tag: false,
7151     abbr: false,
7152     align: false,
7153     axis: false,
7154     bgcolor: false,
7155     charoff: false,
7156     colspan: false,
7157     headers: false,
7158     height: false,
7159     nowrap: false,
7160     rowspan: false,
7161     scope: false,
7162     valign: false,
7163     width: false,
7164     
7165     
7166     getAutoCreate : function(){
7167         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7168         
7169         cfg = {
7170             tag: 'td'
7171         };
7172         
7173         if(this.tag){
7174             cfg.tag = this.tag;
7175         }
7176         
7177         if (this.html) {
7178             cfg.html=this.html
7179         }
7180         if (this.cls) {
7181             cfg.cls=this.cls
7182         }
7183         if (this.abbr) {
7184             cfg.abbr=this.abbr
7185         }
7186         if (this.align) {
7187             cfg.align=this.align
7188         }
7189         if (this.axis) {
7190             cfg.axis=this.axis
7191         }
7192         if (this.bgcolor) {
7193             cfg.bgcolor=this.bgcolor
7194         }
7195         if (this.charoff) {
7196             cfg.charoff=this.charoff
7197         }
7198         if (this.colspan) {
7199             cfg.colspan=this.colspan
7200         }
7201         if (this.headers) {
7202             cfg.headers=this.headers
7203         }
7204         if (this.height) {
7205             cfg.height=this.height
7206         }
7207         if (this.nowrap) {
7208             cfg.nowrap=this.nowrap
7209         }
7210         if (this.rowspan) {
7211             cfg.rowspan=this.rowspan
7212         }
7213         if (this.scope) {
7214             cfg.scope=this.scope
7215         }
7216         if (this.valign) {
7217             cfg.valign=this.valign
7218         }
7219         if (this.width) {
7220             cfg.width=this.width
7221         }
7222         
7223         
7224         return cfg;
7225     }
7226    
7227 });
7228
7229  
7230
7231  /*
7232  * - LGPL
7233  *
7234  * table row
7235  * 
7236  */
7237
7238 /**
7239  * @class Roo.bootstrap.TableRow
7240  * @extends Roo.bootstrap.Component
7241  * Bootstrap TableRow class
7242  * @cfg {String} cls row class
7243  * @cfg {String} align Aligns the content in a table row
7244  * @cfg {String} bgcolor Specifies a background color for a table row
7245  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7246  * @cfg {String} valign Vertical aligns the content in a table row
7247  * 
7248  * @constructor
7249  * Create a new TableRow
7250  * @param {Object} config The config object
7251  */
7252
7253 Roo.bootstrap.TableRow = function(config){
7254     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7255 };
7256
7257 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7258     
7259     cls: false,
7260     align: false,
7261     bgcolor: false,
7262     charoff: false,
7263     valign: false,
7264     
7265     getAutoCreate : function(){
7266         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7267         
7268         cfg = {
7269             tag: 'tr'
7270         };
7271             
7272         if(this.cls){
7273             cfg.cls = this.cls;
7274         }
7275         if(this.align){
7276             cfg.align = this.align;
7277         }
7278         if(this.bgcolor){
7279             cfg.bgcolor = this.bgcolor;
7280         }
7281         if(this.charoff){
7282             cfg.charoff = this.charoff;
7283         }
7284         if(this.valign){
7285             cfg.valign = this.valign;
7286         }
7287         
7288         return cfg;
7289     }
7290    
7291 });
7292
7293  
7294
7295  /*
7296  * - LGPL
7297  *
7298  * table body
7299  * 
7300  */
7301
7302 /**
7303  * @class Roo.bootstrap.TableBody
7304  * @extends Roo.bootstrap.Component
7305  * Bootstrap TableBody class
7306  * @cfg {String} cls element class
7307  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7308  * @cfg {String} align Aligns the content inside the element
7309  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7310  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7311  * 
7312  * @constructor
7313  * Create a new TableBody
7314  * @param {Object} config The config object
7315  */
7316
7317 Roo.bootstrap.TableBody = function(config){
7318     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7319 };
7320
7321 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7322     
7323     cls: false,
7324     tag: false,
7325     align: false,
7326     charoff: false,
7327     valign: false,
7328     
7329     getAutoCreate : function(){
7330         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7331         
7332         cfg = {
7333             tag: 'tbody'
7334         };
7335             
7336         if (this.cls) {
7337             cfg.cls=this.cls
7338         }
7339         if(this.tag){
7340             cfg.tag = this.tag;
7341         }
7342         
7343         if(this.align){
7344             cfg.align = this.align;
7345         }
7346         if(this.charoff){
7347             cfg.charoff = this.charoff;
7348         }
7349         if(this.valign){
7350             cfg.valign = this.valign;
7351         }
7352         
7353         return cfg;
7354     }
7355     
7356     
7357 //    initEvents : function()
7358 //    {
7359 //        
7360 //        if(!this.store){
7361 //            return;
7362 //        }
7363 //        
7364 //        this.store = Roo.factory(this.store, Roo.data);
7365 //        this.store.on('load', this.onLoad, this);
7366 //        
7367 //        this.store.load();
7368 //        
7369 //    },
7370 //    
7371 //    onLoad: function () 
7372 //    {   
7373 //        this.fireEvent('load', this);
7374 //    }
7375 //    
7376 //   
7377 });
7378
7379  
7380
7381  /*
7382  * Based on:
7383  * Ext JS Library 1.1.1
7384  * Copyright(c) 2006-2007, Ext JS, LLC.
7385  *
7386  * Originally Released Under LGPL - original licence link has changed is not relivant.
7387  *
7388  * Fork - LGPL
7389  * <script type="text/javascript">
7390  */
7391
7392 // as we use this in bootstrap.
7393 Roo.namespace('Roo.form');
7394  /**
7395  * @class Roo.form.Action
7396  * Internal Class used to handle form actions
7397  * @constructor
7398  * @param {Roo.form.BasicForm} el The form element or its id
7399  * @param {Object} config Configuration options
7400  */
7401
7402  
7403  
7404 // define the action interface
7405 Roo.form.Action = function(form, options){
7406     this.form = form;
7407     this.options = options || {};
7408 };
7409 /**
7410  * Client Validation Failed
7411  * @const 
7412  */
7413 Roo.form.Action.CLIENT_INVALID = 'client';
7414 /**
7415  * Server Validation Failed
7416  * @const 
7417  */
7418 Roo.form.Action.SERVER_INVALID = 'server';
7419  /**
7420  * Connect to Server Failed
7421  * @const 
7422  */
7423 Roo.form.Action.CONNECT_FAILURE = 'connect';
7424 /**
7425  * Reading Data from Server Failed
7426  * @const 
7427  */
7428 Roo.form.Action.LOAD_FAILURE = 'load';
7429
7430 Roo.form.Action.prototype = {
7431     type : 'default',
7432     failureType : undefined,
7433     response : undefined,
7434     result : undefined,
7435
7436     // interface method
7437     run : function(options){
7438
7439     },
7440
7441     // interface method
7442     success : function(response){
7443
7444     },
7445
7446     // interface method
7447     handleResponse : function(response){
7448
7449     },
7450
7451     // default connection failure
7452     failure : function(response){
7453         
7454         this.response = response;
7455         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7456         this.form.afterAction(this, false);
7457     },
7458
7459     processResponse : function(response){
7460         this.response = response;
7461         if(!response.responseText){
7462             return true;
7463         }
7464         this.result = this.handleResponse(response);
7465         return this.result;
7466     },
7467
7468     // utility functions used internally
7469     getUrl : function(appendParams){
7470         var url = this.options.url || this.form.url || this.form.el.dom.action;
7471         if(appendParams){
7472             var p = this.getParams();
7473             if(p){
7474                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7475             }
7476         }
7477         return url;
7478     },
7479
7480     getMethod : function(){
7481         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7482     },
7483
7484     getParams : function(){
7485         var bp = this.form.baseParams;
7486         var p = this.options.params;
7487         if(p){
7488             if(typeof p == "object"){
7489                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7490             }else if(typeof p == 'string' && bp){
7491                 p += '&' + Roo.urlEncode(bp);
7492             }
7493         }else if(bp){
7494             p = Roo.urlEncode(bp);
7495         }
7496         return p;
7497     },
7498
7499     createCallback : function(){
7500         return {
7501             success: this.success,
7502             failure: this.failure,
7503             scope: this,
7504             timeout: (this.form.timeout*1000),
7505             upload: this.form.fileUpload ? this.success : undefined
7506         };
7507     }
7508 };
7509
7510 Roo.form.Action.Submit = function(form, options){
7511     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7512 };
7513
7514 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7515     type : 'submit',
7516
7517     haveProgress : false,
7518     uploadComplete : false,
7519     
7520     // uploadProgress indicator.
7521     uploadProgress : function()
7522     {
7523         if (!this.form.progressUrl) {
7524             return;
7525         }
7526         
7527         if (!this.haveProgress) {
7528             Roo.MessageBox.progress("Uploading", "Uploading");
7529         }
7530         if (this.uploadComplete) {
7531            Roo.MessageBox.hide();
7532            return;
7533         }
7534         
7535         this.haveProgress = true;
7536    
7537         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7538         
7539         var c = new Roo.data.Connection();
7540         c.request({
7541             url : this.form.progressUrl,
7542             params: {
7543                 id : uid
7544             },
7545             method: 'GET',
7546             success : function(req){
7547                //console.log(data);
7548                 var rdata = false;
7549                 var edata;
7550                 try  {
7551                    rdata = Roo.decode(req.responseText)
7552                 } catch (e) {
7553                     Roo.log("Invalid data from server..");
7554                     Roo.log(edata);
7555                     return;
7556                 }
7557                 if (!rdata || !rdata.success) {
7558                     Roo.log(rdata);
7559                     Roo.MessageBox.alert(Roo.encode(rdata));
7560                     return;
7561                 }
7562                 var data = rdata.data;
7563                 
7564                 if (this.uploadComplete) {
7565                    Roo.MessageBox.hide();
7566                    return;
7567                 }
7568                    
7569                 if (data){
7570                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7571                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7572                     );
7573                 }
7574                 this.uploadProgress.defer(2000,this);
7575             },
7576        
7577             failure: function(data) {
7578                 Roo.log('progress url failed ');
7579                 Roo.log(data);
7580             },
7581             scope : this
7582         });
7583            
7584     },
7585     
7586     
7587     run : function()
7588     {
7589         // run get Values on the form, so it syncs any secondary forms.
7590         this.form.getValues();
7591         
7592         var o = this.options;
7593         var method = this.getMethod();
7594         var isPost = method == 'POST';
7595         if(o.clientValidation === false || this.form.isValid()){
7596             
7597             if (this.form.progressUrl) {
7598                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7599                     (new Date() * 1) + '' + Math.random());
7600                     
7601             } 
7602             
7603             
7604             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7605                 form:this.form.el.dom,
7606                 url:this.getUrl(!isPost),
7607                 method: method,
7608                 params:isPost ? this.getParams() : null,
7609                 isUpload: this.form.fileUpload
7610             }));
7611             
7612             this.uploadProgress();
7613
7614         }else if (o.clientValidation !== false){ // client validation failed
7615             this.failureType = Roo.form.Action.CLIENT_INVALID;
7616             this.form.afterAction(this, false);
7617         }
7618     },
7619
7620     success : function(response)
7621     {
7622         this.uploadComplete= true;
7623         if (this.haveProgress) {
7624             Roo.MessageBox.hide();
7625         }
7626         
7627         
7628         var result = this.processResponse(response);
7629         if(result === true || result.success){
7630             this.form.afterAction(this, true);
7631             return;
7632         }
7633         if(result.errors){
7634             this.form.markInvalid(result.errors);
7635             this.failureType = Roo.form.Action.SERVER_INVALID;
7636         }
7637         this.form.afterAction(this, false);
7638     },
7639     failure : function(response)
7640     {
7641         this.uploadComplete= true;
7642         if (this.haveProgress) {
7643             Roo.MessageBox.hide();
7644         }
7645         
7646         this.response = response;
7647         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7648         this.form.afterAction(this, false);
7649     },
7650     
7651     handleResponse : function(response){
7652         if(this.form.errorReader){
7653             var rs = this.form.errorReader.read(response);
7654             var errors = [];
7655             if(rs.records){
7656                 for(var i = 0, len = rs.records.length; i < len; i++) {
7657                     var r = rs.records[i];
7658                     errors[i] = r.data;
7659                 }
7660             }
7661             if(errors.length < 1){
7662                 errors = null;
7663             }
7664             return {
7665                 success : rs.success,
7666                 errors : errors
7667             };
7668         }
7669         var ret = false;
7670         try {
7671             ret = Roo.decode(response.responseText);
7672         } catch (e) {
7673             ret = {
7674                 success: false,
7675                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7676                 errors : []
7677             };
7678         }
7679         return ret;
7680         
7681     }
7682 });
7683
7684
7685 Roo.form.Action.Load = function(form, options){
7686     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7687     this.reader = this.form.reader;
7688 };
7689
7690 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7691     type : 'load',
7692
7693     run : function(){
7694         
7695         Roo.Ajax.request(Roo.apply(
7696                 this.createCallback(), {
7697                     method:this.getMethod(),
7698                     url:this.getUrl(false),
7699                     params:this.getParams()
7700         }));
7701     },
7702
7703     success : function(response){
7704         
7705         var result = this.processResponse(response);
7706         if(result === true || !result.success || !result.data){
7707             this.failureType = Roo.form.Action.LOAD_FAILURE;
7708             this.form.afterAction(this, false);
7709             return;
7710         }
7711         this.form.clearInvalid();
7712         this.form.setValues(result.data);
7713         this.form.afterAction(this, true);
7714     },
7715
7716     handleResponse : function(response){
7717         if(this.form.reader){
7718             var rs = this.form.reader.read(response);
7719             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7720             return {
7721                 success : rs.success,
7722                 data : data
7723             };
7724         }
7725         return Roo.decode(response.responseText);
7726     }
7727 });
7728
7729 Roo.form.Action.ACTION_TYPES = {
7730     'load' : Roo.form.Action.Load,
7731     'submit' : Roo.form.Action.Submit
7732 };/*
7733  * - LGPL
7734  *
7735  * form
7736  *
7737  */
7738
7739 /**
7740  * @class Roo.bootstrap.Form
7741  * @extends Roo.bootstrap.Component
7742  * Bootstrap Form class
7743  * @cfg {String} method  GET | POST (default POST)
7744  * @cfg {String} labelAlign top | left (default top)
7745  * @cfg {String} align left  | right - for navbars
7746  * @cfg {Boolean} loadMask load mask when submit (default true)
7747
7748  *
7749  * @constructor
7750  * Create a new Form
7751  * @param {Object} config The config object
7752  */
7753
7754
7755 Roo.bootstrap.Form = function(config){
7756     
7757     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7758     
7759     Roo.bootstrap.Form.popover.apply();
7760     
7761     this.addEvents({
7762         /**
7763          * @event clientvalidation
7764          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7765          * @param {Form} this
7766          * @param {Boolean} valid true if the form has passed client-side validation
7767          */
7768         clientvalidation: true,
7769         /**
7770          * @event beforeaction
7771          * Fires before any action is performed. Return false to cancel the action.
7772          * @param {Form} this
7773          * @param {Action} action The action to be performed
7774          */
7775         beforeaction: true,
7776         /**
7777          * @event actionfailed
7778          * Fires when an action fails.
7779          * @param {Form} this
7780          * @param {Action} action The action that failed
7781          */
7782         actionfailed : true,
7783         /**
7784          * @event actioncomplete
7785          * Fires when an action is completed.
7786          * @param {Form} this
7787          * @param {Action} action The action that completed
7788          */
7789         actioncomplete : true
7790     });
7791 };
7792
7793 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7794
7795      /**
7796      * @cfg {String} method
7797      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7798      */
7799     method : 'POST',
7800     /**
7801      * @cfg {String} url
7802      * The URL to use for form actions if one isn't supplied in the action options.
7803      */
7804     /**
7805      * @cfg {Boolean} fileUpload
7806      * Set to true if this form is a file upload.
7807      */
7808
7809     /**
7810      * @cfg {Object} baseParams
7811      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7812      */
7813
7814     /**
7815      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7816      */
7817     timeout: 30,
7818     /**
7819      * @cfg {Sting} align (left|right) for navbar forms
7820      */
7821     align : 'left',
7822
7823     // private
7824     activeAction : null,
7825
7826     /**
7827      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7828      * element by passing it or its id or mask the form itself by passing in true.
7829      * @type Mixed
7830      */
7831     waitMsgTarget : false,
7832
7833     loadMask : true,
7834     
7835     /**
7836      * @cfg {Boolean} errorMask (true|false) default false
7837      */
7838     errorMask : false,
7839     
7840     /**
7841      * @cfg {Number} maskOffset Default 100
7842      */
7843     maskOffset : 100,
7844     
7845     /**
7846      * @cfg {Boolean} maskBody
7847      */
7848     maskBody : false,
7849
7850     getAutoCreate : function(){
7851
7852         var cfg = {
7853             tag: 'form',
7854             method : this.method || 'POST',
7855             id : this.id || Roo.id(),
7856             cls : ''
7857         };
7858         if (this.parent().xtype.match(/^Nav/)) {
7859             cfg.cls = 'navbar-form navbar-' + this.align;
7860
7861         }
7862
7863         if (this.labelAlign == 'left' ) {
7864             cfg.cls += ' form-horizontal';
7865         }
7866
7867
7868         return cfg;
7869     },
7870     initEvents : function()
7871     {
7872         this.el.on('submit', this.onSubmit, this);
7873         // this was added as random key presses on the form where triggering form submit.
7874         this.el.on('keypress', function(e) {
7875             if (e.getCharCode() != 13) {
7876                 return true;
7877             }
7878             // we might need to allow it for textareas.. and some other items.
7879             // check e.getTarget().
7880
7881             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7882                 return true;
7883             }
7884
7885             Roo.log("keypress blocked");
7886
7887             e.preventDefault();
7888             return false;
7889         });
7890         
7891     },
7892     // private
7893     onSubmit : function(e){
7894         e.stopEvent();
7895     },
7896
7897      /**
7898      * Returns true if client-side validation on the form is successful.
7899      * @return Boolean
7900      */
7901     isValid : function(){
7902         var items = this.getItems();
7903         var valid = true;
7904         var target = false;
7905         
7906         items.each(function(f){
7907             
7908             if(f.validate()){
7909                 return;
7910             }
7911             
7912             Roo.log('invalid field: ' + f.name);
7913             
7914             valid = false;
7915
7916             if(!target && f.el.isVisible(true)){
7917                 target = f;
7918             }
7919            
7920         });
7921         
7922         if(this.errorMask && !valid){
7923             Roo.bootstrap.Form.popover.mask(this, target);
7924         }
7925         
7926         return valid;
7927     },
7928     
7929     /**
7930      * Returns true if any fields in this form have changed since their original load.
7931      * @return Boolean
7932      */
7933     isDirty : function(){
7934         var dirty = false;
7935         var items = this.getItems();
7936         items.each(function(f){
7937            if(f.isDirty()){
7938                dirty = true;
7939                return false;
7940            }
7941            return true;
7942         });
7943         return dirty;
7944     },
7945      /**
7946      * Performs a predefined action (submit or load) or custom actions you define on this form.
7947      * @param {String} actionName The name of the action type
7948      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7949      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7950      * accept other config options):
7951      * <pre>
7952 Property          Type             Description
7953 ----------------  ---------------  ----------------------------------------------------------------------------------
7954 url               String           The url for the action (defaults to the form's url)
7955 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7956 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7957 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7958                                    validate the form on the client (defaults to false)
7959      * </pre>
7960      * @return {BasicForm} this
7961      */
7962     doAction : function(action, options){
7963         if(typeof action == 'string'){
7964             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7965         }
7966         if(this.fireEvent('beforeaction', this, action) !== false){
7967             this.beforeAction(action);
7968             action.run.defer(100, action);
7969         }
7970         return this;
7971     },
7972
7973     // private
7974     beforeAction : function(action){
7975         var o = action.options;
7976         
7977         if(this.loadMask){
7978             
7979             if(this.maskBody){
7980                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7981             } else {
7982                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7983             }
7984         }
7985         // not really supported yet.. ??
7986
7987         //if(this.waitMsgTarget === true){
7988         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7989         //}else if(this.waitMsgTarget){
7990         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7991         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7992         //}else {
7993         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7994        // }
7995
7996     },
7997
7998     // private
7999     afterAction : function(action, success){
8000         this.activeAction = null;
8001         var o = action.options;
8002
8003         if(this.loadMask){
8004             
8005             if(this.maskBody){
8006                 Roo.get(document.body).unmask();
8007             } else {
8008                 this.el.unmask();
8009             }
8010         }
8011         
8012         //if(this.waitMsgTarget === true){
8013 //            this.el.unmask();
8014         //}else if(this.waitMsgTarget){
8015         //    this.waitMsgTarget.unmask();
8016         //}else{
8017         //    Roo.MessageBox.updateProgress(1);
8018         //    Roo.MessageBox.hide();
8019        // }
8020         //
8021         if(success){
8022             if(o.reset){
8023                 this.reset();
8024             }
8025             Roo.callback(o.success, o.scope, [this, action]);
8026             this.fireEvent('actioncomplete', this, action);
8027
8028         }else{
8029
8030             // failure condition..
8031             // we have a scenario where updates need confirming.
8032             // eg. if a locking scenario exists..
8033             // we look for { errors : { needs_confirm : true }} in the response.
8034             if (
8035                 (typeof(action.result) != 'undefined')  &&
8036                 (typeof(action.result.errors) != 'undefined')  &&
8037                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8038            ){
8039                 var _t = this;
8040                 Roo.log("not supported yet");
8041                  /*
8042
8043                 Roo.MessageBox.confirm(
8044                     "Change requires confirmation",
8045                     action.result.errorMsg,
8046                     function(r) {
8047                         if (r != 'yes') {
8048                             return;
8049                         }
8050                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8051                     }
8052
8053                 );
8054                 */
8055
8056
8057                 return;
8058             }
8059
8060             Roo.callback(o.failure, o.scope, [this, action]);
8061             // show an error message if no failed handler is set..
8062             if (!this.hasListener('actionfailed')) {
8063                 Roo.log("need to add dialog support");
8064                 /*
8065                 Roo.MessageBox.alert("Error",
8066                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8067                         action.result.errorMsg :
8068                         "Saving Failed, please check your entries or try again"
8069                 );
8070                 */
8071             }
8072
8073             this.fireEvent('actionfailed', this, action);
8074         }
8075
8076     },
8077     /**
8078      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8079      * @param {String} id The value to search for
8080      * @return Field
8081      */
8082     findField : function(id){
8083         var items = this.getItems();
8084         var field = items.get(id);
8085         if(!field){
8086              items.each(function(f){
8087                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8088                     field = f;
8089                     return false;
8090                 }
8091                 return true;
8092             });
8093         }
8094         return field || null;
8095     },
8096      /**
8097      * Mark fields in this form invalid in bulk.
8098      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8099      * @return {BasicForm} this
8100      */
8101     markInvalid : function(errors){
8102         if(errors instanceof Array){
8103             for(var i = 0, len = errors.length; i < len; i++){
8104                 var fieldError = errors[i];
8105                 var f = this.findField(fieldError.id);
8106                 if(f){
8107                     f.markInvalid(fieldError.msg);
8108                 }
8109             }
8110         }else{
8111             var field, id;
8112             for(id in errors){
8113                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8114                     field.markInvalid(errors[id]);
8115                 }
8116             }
8117         }
8118         //Roo.each(this.childForms || [], function (f) {
8119         //    f.markInvalid(errors);
8120         //});
8121
8122         return this;
8123     },
8124
8125     /**
8126      * Set values for fields in this form in bulk.
8127      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8128      * @return {BasicForm} this
8129      */
8130     setValues : function(values){
8131         if(values instanceof Array){ // array of objects
8132             for(var i = 0, len = values.length; i < len; i++){
8133                 var v = values[i];
8134                 var f = this.findField(v.id);
8135                 if(f){
8136                     f.setValue(v.value);
8137                     if(this.trackResetOnLoad){
8138                         f.originalValue = f.getValue();
8139                     }
8140                 }
8141             }
8142         }else{ // object hash
8143             var field, id;
8144             for(id in values){
8145                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8146
8147                     if (field.setFromData &&
8148                         field.valueField &&
8149                         field.displayField &&
8150                         // combos' with local stores can
8151                         // be queried via setValue()
8152                         // to set their value..
8153                         (field.store && !field.store.isLocal)
8154                         ) {
8155                         // it's a combo
8156                         var sd = { };
8157                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8158                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8159                         field.setFromData(sd);
8160
8161                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8162                         
8163                         field.setFromData(values);
8164                         
8165                     } else {
8166                         field.setValue(values[id]);
8167                     }
8168
8169
8170                     if(this.trackResetOnLoad){
8171                         field.originalValue = field.getValue();
8172                     }
8173                 }
8174             }
8175         }
8176
8177         //Roo.each(this.childForms || [], function (f) {
8178         //    f.setValues(values);
8179         //});
8180
8181         return this;
8182     },
8183
8184     /**
8185      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8186      * they are returned as an array.
8187      * @param {Boolean} asString
8188      * @return {Object}
8189      */
8190     getValues : function(asString){
8191         //if (this.childForms) {
8192             // copy values from the child forms
8193         //    Roo.each(this.childForms, function (f) {
8194         //        this.setValues(f.getValues());
8195         //    }, this);
8196         //}
8197
8198
8199
8200         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8201         if(asString === true){
8202             return fs;
8203         }
8204         return Roo.urlDecode(fs);
8205     },
8206
8207     /**
8208      * Returns the fields in this form as an object with key/value pairs.
8209      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8210      * @return {Object}
8211      */
8212     getFieldValues : function(with_hidden)
8213     {
8214         var items = this.getItems();
8215         var ret = {};
8216         items.each(function(f){
8217             
8218             if (!f.getName()) {
8219                 return;
8220             }
8221             
8222             var v = f.getValue();
8223             
8224             if (f.inputType =='radio') {
8225                 if (typeof(ret[f.getName()]) == 'undefined') {
8226                     ret[f.getName()] = ''; // empty..
8227                 }
8228
8229                 if (!f.el.dom.checked) {
8230                     return;
8231
8232                 }
8233                 v = f.el.dom.value;
8234
8235             }
8236             
8237             if(f.xtype == 'MoneyField'){
8238                 ret[f.currencyName] = f.getCurrency();
8239             }
8240
8241             // not sure if this supported any more..
8242             if ((typeof(v) == 'object') && f.getRawValue) {
8243                 v = f.getRawValue() ; // dates..
8244             }
8245             // combo boxes where name != hiddenName...
8246             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8247                 ret[f.name] = f.getRawValue();
8248             }
8249             ret[f.getName()] = v;
8250         });
8251
8252         return ret;
8253     },
8254
8255     /**
8256      * Clears all invalid messages in this form.
8257      * @return {BasicForm} this
8258      */
8259     clearInvalid : function(){
8260         var items = this.getItems();
8261
8262         items.each(function(f){
8263            f.clearInvalid();
8264         });
8265
8266         return this;
8267     },
8268
8269     /**
8270      * Resets this form.
8271      * @return {BasicForm} this
8272      */
8273     reset : function(){
8274         var items = this.getItems();
8275         items.each(function(f){
8276             f.reset();
8277         });
8278
8279         Roo.each(this.childForms || [], function (f) {
8280             f.reset();
8281         });
8282
8283
8284         return this;
8285     },
8286     
8287     getItems : function()
8288     {
8289         var r=new Roo.util.MixedCollection(false, function(o){
8290             return o.id || (o.id = Roo.id());
8291         });
8292         var iter = function(el) {
8293             if (el.inputEl) {
8294                 r.add(el);
8295             }
8296             if (!el.items) {
8297                 return;
8298             }
8299             Roo.each(el.items,function(e) {
8300                 iter(e);
8301             });
8302         };
8303
8304         iter(this);
8305         return r;
8306     },
8307     
8308     hideFields : function(items)
8309     {
8310         Roo.each(items, function(i){
8311             
8312             var f = this.findField(i);
8313             
8314             if(!f){
8315                 return;
8316             }
8317             
8318             f.hide();
8319             
8320         }, this);
8321     },
8322     
8323     showFields : function(items)
8324     {
8325         Roo.each(items, function(i){
8326             
8327             var f = this.findField(i);
8328             
8329             if(!f){
8330                 return;
8331             }
8332             
8333             f.show();
8334             
8335         }, this);
8336     }
8337
8338 });
8339
8340 Roo.apply(Roo.bootstrap.Form, {
8341     
8342     popover : {
8343         
8344         padding : 5,
8345         
8346         isApplied : false,
8347         
8348         isMasked : false,
8349         
8350         form : false,
8351         
8352         target : false,
8353         
8354         toolTip : false,
8355         
8356         intervalID : false,
8357         
8358         maskEl : false,
8359         
8360         apply : function()
8361         {
8362             if(this.isApplied){
8363                 return;
8364             }
8365             
8366             this.maskEl = {
8367                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8368                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8369                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8370                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8371             };
8372             
8373             this.maskEl.top.enableDisplayMode("block");
8374             this.maskEl.left.enableDisplayMode("block");
8375             this.maskEl.bottom.enableDisplayMode("block");
8376             this.maskEl.right.enableDisplayMode("block");
8377             
8378             this.toolTip = new Roo.bootstrap.Tooltip({
8379                 cls : 'roo-form-error-popover',
8380                 alignment : {
8381                     'left' : ['r-l', [-2,0], 'right'],
8382                     'right' : ['l-r', [2,0], 'left'],
8383                     'bottom' : ['tl-bl', [0,2], 'top'],
8384                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8385                 }
8386             });
8387             
8388             this.toolTip.render(Roo.get(document.body));
8389
8390             this.toolTip.el.enableDisplayMode("block");
8391             
8392             Roo.get(document.body).on('click', function(){
8393                 this.unmask();
8394             }, this);
8395             
8396             Roo.get(document.body).on('touchstart', function(){
8397                 this.unmask();
8398             }, this);
8399             
8400             this.isApplied = true
8401         },
8402         
8403         mask : function(form, target)
8404         {
8405             this.form = form;
8406             
8407             this.target = target;
8408             
8409             if(!this.form.errorMask || !target.el){
8410                 return;
8411             }
8412             
8413             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8414             
8415             Roo.log(scrollable);
8416             
8417             var ot = this.target.el.calcOffsetsTo(scrollable);
8418             
8419             var scrollTo = ot[1] - this.form.maskOffset;
8420             
8421             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8422             
8423             scrollable.scrollTo('top', scrollTo);
8424             
8425             var box = this.target.el.getBox();
8426             Roo.log(box);
8427             var zIndex = Roo.bootstrap.Modal.zIndex++;
8428
8429             
8430             this.maskEl.top.setStyle('position', 'absolute');
8431             this.maskEl.top.setStyle('z-index', zIndex);
8432             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8433             this.maskEl.top.setLeft(0);
8434             this.maskEl.top.setTop(0);
8435             this.maskEl.top.show();
8436             
8437             this.maskEl.left.setStyle('position', 'absolute');
8438             this.maskEl.left.setStyle('z-index', zIndex);
8439             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8440             this.maskEl.left.setLeft(0);
8441             this.maskEl.left.setTop(box.y - this.padding);
8442             this.maskEl.left.show();
8443
8444             this.maskEl.bottom.setStyle('position', 'absolute');
8445             this.maskEl.bottom.setStyle('z-index', zIndex);
8446             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8447             this.maskEl.bottom.setLeft(0);
8448             this.maskEl.bottom.setTop(box.bottom + this.padding);
8449             this.maskEl.bottom.show();
8450
8451             this.maskEl.right.setStyle('position', 'absolute');
8452             this.maskEl.right.setStyle('z-index', zIndex);
8453             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8454             this.maskEl.right.setLeft(box.right + this.padding);
8455             this.maskEl.right.setTop(box.y - this.padding);
8456             this.maskEl.right.show();
8457
8458             this.toolTip.bindEl = this.target.el;
8459
8460             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8461
8462             var tip = this.target.blankText;
8463
8464             if(this.target.getValue() !== '' ) {
8465                 
8466                 if (this.target.invalidText.length) {
8467                     tip = this.target.invalidText;
8468                 } else if (this.target.regexText.length){
8469                     tip = this.target.regexText;
8470                 }
8471             }
8472
8473             this.toolTip.show(tip);
8474
8475             this.intervalID = window.setInterval(function() {
8476                 Roo.bootstrap.Form.popover.unmask();
8477             }, 10000);
8478
8479             window.onwheel = function(){ return false;};
8480             
8481             (function(){ this.isMasked = true; }).defer(500, this);
8482             
8483         },
8484         
8485         unmask : function()
8486         {
8487             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8488                 return;
8489             }
8490             
8491             this.maskEl.top.setStyle('position', 'absolute');
8492             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8493             this.maskEl.top.hide();
8494
8495             this.maskEl.left.setStyle('position', 'absolute');
8496             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8497             this.maskEl.left.hide();
8498
8499             this.maskEl.bottom.setStyle('position', 'absolute');
8500             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8501             this.maskEl.bottom.hide();
8502
8503             this.maskEl.right.setStyle('position', 'absolute');
8504             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8505             this.maskEl.right.hide();
8506             
8507             this.toolTip.hide();
8508             
8509             this.toolTip.el.hide();
8510             
8511             window.onwheel = function(){ return true;};
8512             
8513             if(this.intervalID){
8514                 window.clearInterval(this.intervalID);
8515                 this.intervalID = false;
8516             }
8517             
8518             this.isMasked = false;
8519             
8520         }
8521         
8522     }
8523     
8524 });
8525
8526 /*
8527  * Based on:
8528  * Ext JS Library 1.1.1
8529  * Copyright(c) 2006-2007, Ext JS, LLC.
8530  *
8531  * Originally Released Under LGPL - original licence link has changed is not relivant.
8532  *
8533  * Fork - LGPL
8534  * <script type="text/javascript">
8535  */
8536 /**
8537  * @class Roo.form.VTypes
8538  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8539  * @singleton
8540  */
8541 Roo.form.VTypes = function(){
8542     // closure these in so they are only created once.
8543     var alpha = /^[a-zA-Z_]+$/;
8544     var alphanum = /^[a-zA-Z0-9_]+$/;
8545     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8546     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8547
8548     // All these messages and functions are configurable
8549     return {
8550         /**
8551          * The function used to validate email addresses
8552          * @param {String} value The email address
8553          */
8554         'email' : function(v){
8555             return email.test(v);
8556         },
8557         /**
8558          * The error text to display when the email validation function returns false
8559          * @type String
8560          */
8561         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8562         /**
8563          * The keystroke filter mask to be applied on email input
8564          * @type RegExp
8565          */
8566         'emailMask' : /[a-z0-9_\.\-@]/i,
8567
8568         /**
8569          * The function used to validate URLs
8570          * @param {String} value The URL
8571          */
8572         'url' : function(v){
8573             return url.test(v);
8574         },
8575         /**
8576          * The error text to display when the url validation function returns false
8577          * @type String
8578          */
8579         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8580         
8581         /**
8582          * The function used to validate alpha values
8583          * @param {String} value The value
8584          */
8585         'alpha' : function(v){
8586             return alpha.test(v);
8587         },
8588         /**
8589          * The error text to display when the alpha validation function returns false
8590          * @type String
8591          */
8592         'alphaText' : 'This field should only contain letters and _',
8593         /**
8594          * The keystroke filter mask to be applied on alpha input
8595          * @type RegExp
8596          */
8597         'alphaMask' : /[a-z_]/i,
8598
8599         /**
8600          * The function used to validate alphanumeric values
8601          * @param {String} value The value
8602          */
8603         'alphanum' : function(v){
8604             return alphanum.test(v);
8605         },
8606         /**
8607          * The error text to display when the alphanumeric validation function returns false
8608          * @type String
8609          */
8610         'alphanumText' : 'This field should only contain letters, numbers and _',
8611         /**
8612          * The keystroke filter mask to be applied on alphanumeric input
8613          * @type RegExp
8614          */
8615         'alphanumMask' : /[a-z0-9_]/i
8616     };
8617 }();/*
8618  * - LGPL
8619  *
8620  * Input
8621  * 
8622  */
8623
8624 /**
8625  * @class Roo.bootstrap.Input
8626  * @extends Roo.bootstrap.Component
8627  * Bootstrap Input class
8628  * @cfg {Boolean} disabled is it disabled
8629  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8630  * @cfg {String} name name of the input
8631  * @cfg {string} fieldLabel - the label associated
8632  * @cfg {string} placeholder - placeholder to put in text.
8633  * @cfg {string}  before - input group add on before
8634  * @cfg {string} after - input group add on after
8635  * @cfg {string} size - (lg|sm) or leave empty..
8636  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8637  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8638  * @cfg {Number} md colspan out of 12 for computer-sized screens
8639  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8640  * @cfg {string} value default value of the input
8641  * @cfg {Number} labelWidth set the width of label 
8642  * @cfg {Number} labellg set the width of label (1-12)
8643  * @cfg {Number} labelmd set the width of label (1-12)
8644  * @cfg {Number} labelsm set the width of label (1-12)
8645  * @cfg {Number} labelxs set the width of label (1-12)
8646  * @cfg {String} labelAlign (top|left)
8647  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8648  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8649  * @cfg {String} indicatorpos (left|right) default left
8650  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8651  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8652
8653  * @cfg {String} align (left|center|right) Default left
8654  * @cfg {Boolean} forceFeedback (true|false) Default false
8655  * 
8656  * @constructor
8657  * Create a new Input
8658  * @param {Object} config The config object
8659  */
8660
8661 Roo.bootstrap.Input = function(config){
8662     
8663     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8664     
8665     this.addEvents({
8666         /**
8667          * @event focus
8668          * Fires when this field receives input focus.
8669          * @param {Roo.form.Field} this
8670          */
8671         focus : true,
8672         /**
8673          * @event blur
8674          * Fires when this field loses input focus.
8675          * @param {Roo.form.Field} this
8676          */
8677         blur : true,
8678         /**
8679          * @event specialkey
8680          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8681          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8682          * @param {Roo.form.Field} this
8683          * @param {Roo.EventObject} e The event object
8684          */
8685         specialkey : true,
8686         /**
8687          * @event change
8688          * Fires just before the field blurs if the field value has changed.
8689          * @param {Roo.form.Field} this
8690          * @param {Mixed} newValue The new value
8691          * @param {Mixed} oldValue The original value
8692          */
8693         change : true,
8694         /**
8695          * @event invalid
8696          * Fires after the field has been marked as invalid.
8697          * @param {Roo.form.Field} this
8698          * @param {String} msg The validation message
8699          */
8700         invalid : true,
8701         /**
8702          * @event valid
8703          * Fires after the field has been validated with no errors.
8704          * @param {Roo.form.Field} this
8705          */
8706         valid : true,
8707          /**
8708          * @event keyup
8709          * Fires after the key up
8710          * @param {Roo.form.Field} this
8711          * @param {Roo.EventObject}  e The event Object
8712          */
8713         keyup : true
8714     });
8715 };
8716
8717 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8718      /**
8719      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8720       automatic validation (defaults to "keyup").
8721      */
8722     validationEvent : "keyup",
8723      /**
8724      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8725      */
8726     validateOnBlur : true,
8727     /**
8728      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8729      */
8730     validationDelay : 250,
8731      /**
8732      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8733      */
8734     focusClass : "x-form-focus",  // not needed???
8735     
8736        
8737     /**
8738      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8739      */
8740     invalidClass : "has-warning",
8741     
8742     /**
8743      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8744      */
8745     validClass : "has-success",
8746     
8747     /**
8748      * @cfg {Boolean} hasFeedback (true|false) default true
8749      */
8750     hasFeedback : true,
8751     
8752     /**
8753      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8754      */
8755     invalidFeedbackClass : "glyphicon-warning-sign",
8756     
8757     /**
8758      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8759      */
8760     validFeedbackClass : "glyphicon-ok",
8761     
8762     /**
8763      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8764      */
8765     selectOnFocus : false,
8766     
8767      /**
8768      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8769      */
8770     maskRe : null,
8771        /**
8772      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8773      */
8774     vtype : null,
8775     
8776       /**
8777      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8778      */
8779     disableKeyFilter : false,
8780     
8781        /**
8782      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8783      */
8784     disabled : false,
8785      /**
8786      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8787      */
8788     allowBlank : true,
8789     /**
8790      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8791      */
8792     blankText : "Please complete this mandatory field",
8793     
8794      /**
8795      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8796      */
8797     minLength : 0,
8798     /**
8799      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8800      */
8801     maxLength : Number.MAX_VALUE,
8802     /**
8803      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8804      */
8805     minLengthText : "The minimum length for this field is {0}",
8806     /**
8807      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8808      */
8809     maxLengthText : "The maximum length for this field is {0}",
8810   
8811     
8812     /**
8813      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8814      * If available, this function will be called only after the basic validators all return true, and will be passed the
8815      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8816      */
8817     validator : null,
8818     /**
8819      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8820      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8821      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8822      */
8823     regex : null,
8824     /**
8825      * @cfg {String} regexText -- Depricated - use Invalid Text
8826      */
8827     regexText : "",
8828     
8829     /**
8830      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8831      */
8832     invalidText : "",
8833     
8834     
8835     
8836     autocomplete: false,
8837     
8838     
8839     fieldLabel : '',
8840     inputType : 'text',
8841     
8842     name : false,
8843     placeholder: false,
8844     before : false,
8845     after : false,
8846     size : false,
8847     hasFocus : false,
8848     preventMark: false,
8849     isFormField : true,
8850     value : '',
8851     labelWidth : 2,
8852     labelAlign : false,
8853     readOnly : false,
8854     align : false,
8855     formatedValue : false,
8856     forceFeedback : false,
8857     
8858     indicatorpos : 'left',
8859     
8860     labellg : 0,
8861     labelmd : 0,
8862     labelsm : 0,
8863     labelxs : 0,
8864     
8865     capture : '',
8866     accept : '',
8867     
8868     parentLabelAlign : function()
8869     {
8870         var parent = this;
8871         while (parent.parent()) {
8872             parent = parent.parent();
8873             if (typeof(parent.labelAlign) !='undefined') {
8874                 return parent.labelAlign;
8875             }
8876         }
8877         return 'left';
8878         
8879     },
8880     
8881     getAutoCreate : function()
8882     {
8883         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8884         
8885         var id = Roo.id();
8886         
8887         var cfg = {};
8888         
8889         if(this.inputType != 'hidden'){
8890             cfg.cls = 'form-group' //input-group
8891         }
8892         
8893         var input =  {
8894             tag: 'input',
8895             id : id,
8896             type : this.inputType,
8897             value : this.value,
8898             cls : 'form-control',
8899             placeholder : this.placeholder || '',
8900             autocomplete : this.autocomplete || 'new-password'
8901         };
8902         
8903         if(this.capture.length){
8904             input.capture = this.capture;
8905         }
8906         
8907         if(this.accept.length){
8908             input.accept = this.accept + "/*";
8909         }
8910         
8911         if(this.align){
8912             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8913         }
8914         
8915         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8916             input.maxLength = this.maxLength;
8917         }
8918         
8919         if (this.disabled) {
8920             input.disabled=true;
8921         }
8922         
8923         if (this.readOnly) {
8924             input.readonly=true;
8925         }
8926         
8927         if (this.name) {
8928             input.name = this.name;
8929         }
8930         
8931         if (this.size) {
8932             input.cls += ' input-' + this.size;
8933         }
8934         
8935         var settings=this;
8936         ['xs','sm','md','lg'].map(function(size){
8937             if (settings[size]) {
8938                 cfg.cls += ' col-' + size + '-' + settings[size];
8939             }
8940         });
8941         
8942         var inputblock = input;
8943         
8944         var feedback = {
8945             tag: 'span',
8946             cls: 'glyphicon form-control-feedback'
8947         };
8948             
8949         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8950             
8951             inputblock = {
8952                 cls : 'has-feedback',
8953                 cn :  [
8954                     input,
8955                     feedback
8956                 ] 
8957             };  
8958         }
8959         
8960         if (this.before || this.after) {
8961             
8962             inputblock = {
8963                 cls : 'input-group',
8964                 cn :  [] 
8965             };
8966             
8967             if (this.before && typeof(this.before) == 'string') {
8968                 
8969                 inputblock.cn.push({
8970                     tag :'span',
8971                     cls : 'roo-input-before input-group-addon',
8972                     html : this.before
8973                 });
8974             }
8975             if (this.before && typeof(this.before) == 'object') {
8976                 this.before = Roo.factory(this.before);
8977                 
8978                 inputblock.cn.push({
8979                     tag :'span',
8980                     cls : 'roo-input-before input-group-' +
8981                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8982                 });
8983             }
8984             
8985             inputblock.cn.push(input);
8986             
8987             if (this.after && typeof(this.after) == 'string') {
8988                 inputblock.cn.push({
8989                     tag :'span',
8990                     cls : 'roo-input-after input-group-addon',
8991                     html : this.after
8992                 });
8993             }
8994             if (this.after && typeof(this.after) == 'object') {
8995                 this.after = Roo.factory(this.after);
8996                 
8997                 inputblock.cn.push({
8998                     tag :'span',
8999                     cls : 'roo-input-after input-group-' +
9000                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9001                 });
9002             }
9003             
9004             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9005                 inputblock.cls += ' has-feedback';
9006                 inputblock.cn.push(feedback);
9007             }
9008         };
9009         
9010         if (align ==='left' && this.fieldLabel.length) {
9011             
9012             cfg.cls += ' roo-form-group-label-left';
9013             
9014             cfg.cn = [
9015                 {
9016                     tag : 'i',
9017                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9018                     tooltip : 'This field is required'
9019                 },
9020                 {
9021                     tag: 'label',
9022                     'for' :  id,
9023                     cls : 'control-label',
9024                     html : this.fieldLabel
9025
9026                 },
9027                 {
9028                     cls : "", 
9029                     cn: [
9030                         inputblock
9031                     ]
9032                 }
9033             ];
9034             
9035             var labelCfg = cfg.cn[1];
9036             var contentCfg = cfg.cn[2];
9037             
9038             if(this.indicatorpos == 'right'){
9039                 cfg.cn = [
9040                     {
9041                         tag: 'label',
9042                         'for' :  id,
9043                         cls : 'control-label',
9044                         cn : [
9045                             {
9046                                 tag : 'span',
9047                                 html : this.fieldLabel
9048                             },
9049                             {
9050                                 tag : 'i',
9051                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9052                                 tooltip : 'This field is required'
9053                             }
9054                         ]
9055                     },
9056                     {
9057                         cls : "",
9058                         cn: [
9059                             inputblock
9060                         ]
9061                     }
9062
9063                 ];
9064                 
9065                 labelCfg = cfg.cn[0];
9066                 contentCfg = cfg.cn[1];
9067             
9068             }
9069             
9070             if(this.labelWidth > 12){
9071                 labelCfg.style = "width: " + this.labelWidth + 'px';
9072             }
9073             
9074             if(this.labelWidth < 13 && this.labelmd == 0){
9075                 this.labelmd = this.labelWidth;
9076             }
9077             
9078             if(this.labellg > 0){
9079                 labelCfg.cls += ' col-lg-' + this.labellg;
9080                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9081             }
9082             
9083             if(this.labelmd > 0){
9084                 labelCfg.cls += ' col-md-' + this.labelmd;
9085                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9086             }
9087             
9088             if(this.labelsm > 0){
9089                 labelCfg.cls += ' col-sm-' + this.labelsm;
9090                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9091             }
9092             
9093             if(this.labelxs > 0){
9094                 labelCfg.cls += ' col-xs-' + this.labelxs;
9095                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9096             }
9097             
9098             
9099         } else if ( this.fieldLabel.length) {
9100                 
9101             cfg.cn = [
9102                 {
9103                     tag : 'i',
9104                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9105                     tooltip : 'This field is required'
9106                 },
9107                 {
9108                     tag: 'label',
9109                    //cls : 'input-group-addon',
9110                     html : this.fieldLabel
9111
9112                 },
9113
9114                inputblock
9115
9116            ];
9117            
9118            if(this.indicatorpos == 'right'){
9119                 
9120                 cfg.cn = [
9121                     {
9122                         tag: 'label',
9123                        //cls : 'input-group-addon',
9124                         html : this.fieldLabel
9125
9126                     },
9127                     {
9128                         tag : 'i',
9129                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9130                         tooltip : 'This field is required'
9131                     },
9132
9133                    inputblock
9134
9135                ];
9136
9137             }
9138
9139         } else {
9140             
9141             cfg.cn = [
9142
9143                     inputblock
9144
9145             ];
9146                 
9147                 
9148         };
9149         
9150         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9151            cfg.cls += ' navbar-form';
9152         }
9153         
9154         if (this.parentType === 'NavGroup') {
9155            cfg.cls += ' navbar-form';
9156            cfg.tag = 'li';
9157         }
9158         
9159         return cfg;
9160         
9161     },
9162     /**
9163      * return the real input element.
9164      */
9165     inputEl: function ()
9166     {
9167         return this.el.select('input.form-control',true).first();
9168     },
9169     
9170     tooltipEl : function()
9171     {
9172         return this.inputEl();
9173     },
9174     
9175     indicatorEl : function()
9176     {
9177         var indicator = this.el.select('i.roo-required-indicator',true).first();
9178         
9179         if(!indicator){
9180             return false;
9181         }
9182         
9183         return indicator;
9184         
9185     },
9186     
9187     setDisabled : function(v)
9188     {
9189         var i  = this.inputEl().dom;
9190         if (!v) {
9191             i.removeAttribute('disabled');
9192             return;
9193             
9194         }
9195         i.setAttribute('disabled','true');
9196     },
9197     initEvents : function()
9198     {
9199           
9200         this.inputEl().on("keydown" , this.fireKey,  this);
9201         this.inputEl().on("focus", this.onFocus,  this);
9202         this.inputEl().on("blur", this.onBlur,  this);
9203         
9204         this.inputEl().relayEvent('keyup', this);
9205         
9206         this.indicator = this.indicatorEl();
9207         
9208         if(this.indicator){
9209             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9210         }
9211  
9212         // reference to original value for reset
9213         this.originalValue = this.getValue();
9214         //Roo.form.TextField.superclass.initEvents.call(this);
9215         if(this.validationEvent == 'keyup'){
9216             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9217             this.inputEl().on('keyup', this.filterValidation, this);
9218         }
9219         else if(this.validationEvent !== false){
9220             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9221         }
9222         
9223         if(this.selectOnFocus){
9224             this.on("focus", this.preFocus, this);
9225             
9226         }
9227         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9228             this.inputEl().on("keypress", this.filterKeys, this);
9229         } else {
9230             this.inputEl().relayEvent('keypress', this);
9231         }
9232        /* if(this.grow){
9233             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9234             this.el.on("click", this.autoSize,  this);
9235         }
9236         */
9237         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9238             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9239         }
9240         
9241         if (typeof(this.before) == 'object') {
9242             this.before.render(this.el.select('.roo-input-before',true).first());
9243         }
9244         if (typeof(this.after) == 'object') {
9245             this.after.render(this.el.select('.roo-input-after',true).first());
9246         }
9247         
9248         this.inputEl().on('change', this.onChange, this);
9249         
9250     },
9251     filterValidation : function(e){
9252         if(!e.isNavKeyPress()){
9253             this.validationTask.delay(this.validationDelay);
9254         }
9255     },
9256      /**
9257      * Validates the field value
9258      * @return {Boolean} True if the value is valid, else false
9259      */
9260     validate : function(){
9261         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9262         if(this.disabled || this.validateValue(this.getRawValue())){
9263             this.markValid();
9264             return true;
9265         }
9266         
9267         this.markInvalid();
9268         return false;
9269     },
9270     
9271     
9272     /**
9273      * Validates a value according to the field's validation rules and marks the field as invalid
9274      * if the validation fails
9275      * @param {Mixed} value The value to validate
9276      * @return {Boolean} True if the value is valid, else false
9277      */
9278     validateValue : function(value)
9279     {
9280         if(this.getVisibilityEl().hasClass('hidden')){
9281             return true;
9282         }
9283         
9284         if(value.length < 1)  { // if it's blank
9285             if(this.allowBlank){
9286                 return true;
9287             }
9288             return false;
9289         }
9290         
9291         if(value.length < this.minLength){
9292             return false;
9293         }
9294         if(value.length > this.maxLength){
9295             return false;
9296         }
9297         if(this.vtype){
9298             var vt = Roo.form.VTypes;
9299             if(!vt[this.vtype](value, this)){
9300                 return false;
9301             }
9302         }
9303         if(typeof this.validator == "function"){
9304             var msg = this.validator(value);
9305             if(msg !== true){
9306                 return false;
9307             }
9308             if (typeof(msg) == 'string') {
9309                 this.invalidText = msg;
9310             }
9311         }
9312         
9313         if(this.regex && !this.regex.test(value)){
9314             return false;
9315         }
9316         
9317         return true;
9318     },
9319     
9320      // private
9321     fireKey : function(e){
9322         //Roo.log('field ' + e.getKey());
9323         if(e.isNavKeyPress()){
9324             this.fireEvent("specialkey", this, e);
9325         }
9326     },
9327     focus : function (selectText){
9328         if(this.rendered){
9329             this.inputEl().focus();
9330             if(selectText === true){
9331                 this.inputEl().dom.select();
9332             }
9333         }
9334         return this;
9335     } ,
9336     
9337     onFocus : function(){
9338         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9339            // this.el.addClass(this.focusClass);
9340         }
9341         if(!this.hasFocus){
9342             this.hasFocus = true;
9343             this.startValue = this.getValue();
9344             this.fireEvent("focus", this);
9345         }
9346     },
9347     
9348     beforeBlur : Roo.emptyFn,
9349
9350     
9351     // private
9352     onBlur : function(){
9353         this.beforeBlur();
9354         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9355             //this.el.removeClass(this.focusClass);
9356         }
9357         this.hasFocus = false;
9358         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9359             this.validate();
9360         }
9361         var v = this.getValue();
9362         if(String(v) !== String(this.startValue)){
9363             this.fireEvent('change', this, v, this.startValue);
9364         }
9365         this.fireEvent("blur", this);
9366     },
9367     
9368     onChange : function(e)
9369     {
9370         var v = this.getValue();
9371         if(String(v) !== String(this.startValue)){
9372             this.fireEvent('change', this, v, this.startValue);
9373         }
9374         
9375     },
9376     
9377     /**
9378      * Resets the current field value to the originally loaded value and clears any validation messages
9379      */
9380     reset : function(){
9381         this.setValue(this.originalValue);
9382         this.validate();
9383     },
9384      /**
9385      * Returns the name of the field
9386      * @return {Mixed} name The name field
9387      */
9388     getName: function(){
9389         return this.name;
9390     },
9391      /**
9392      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9393      * @return {Mixed} value The field value
9394      */
9395     getValue : function(){
9396         
9397         var v = this.inputEl().getValue();
9398         
9399         return v;
9400     },
9401     /**
9402      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9403      * @return {Mixed} value The field value
9404      */
9405     getRawValue : function(){
9406         var v = this.inputEl().getValue();
9407         
9408         return v;
9409     },
9410     
9411     /**
9412      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9413      * @param {Mixed} value The value to set
9414      */
9415     setRawValue : function(v){
9416         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9417     },
9418     
9419     selectText : function(start, end){
9420         var v = this.getRawValue();
9421         if(v.length > 0){
9422             start = start === undefined ? 0 : start;
9423             end = end === undefined ? v.length : end;
9424             var d = this.inputEl().dom;
9425             if(d.setSelectionRange){
9426                 d.setSelectionRange(start, end);
9427             }else if(d.createTextRange){
9428                 var range = d.createTextRange();
9429                 range.moveStart("character", start);
9430                 range.moveEnd("character", v.length-end);
9431                 range.select();
9432             }
9433         }
9434     },
9435     
9436     /**
9437      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9438      * @param {Mixed} value The value to set
9439      */
9440     setValue : function(v){
9441         this.value = v;
9442         if(this.rendered){
9443             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9444             this.validate();
9445         }
9446     },
9447     
9448     /*
9449     processValue : function(value){
9450         if(this.stripCharsRe){
9451             var newValue = value.replace(this.stripCharsRe, '');
9452             if(newValue !== value){
9453                 this.setRawValue(newValue);
9454                 return newValue;
9455             }
9456         }
9457         return value;
9458     },
9459   */
9460     preFocus : function(){
9461         
9462         if(this.selectOnFocus){
9463             this.inputEl().dom.select();
9464         }
9465     },
9466     filterKeys : function(e){
9467         var k = e.getKey();
9468         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9469             return;
9470         }
9471         var c = e.getCharCode(), cc = String.fromCharCode(c);
9472         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9473             return;
9474         }
9475         if(!this.maskRe.test(cc)){
9476             e.stopEvent();
9477         }
9478     },
9479      /**
9480      * Clear any invalid styles/messages for this field
9481      */
9482     clearInvalid : function(){
9483         
9484         if(!this.el || this.preventMark){ // not rendered
9485             return;
9486         }
9487         
9488      
9489         this.el.removeClass(this.invalidClass);
9490         
9491         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9492             
9493             var feedback = this.el.select('.form-control-feedback', true).first();
9494             
9495             if(feedback){
9496                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9497             }
9498             
9499         }
9500         
9501         if(this.indicator){
9502             this.indicator.removeClass('visible');
9503             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9504         }
9505         
9506         this.fireEvent('valid', this);
9507     },
9508     
9509      /**
9510      * Mark this field as valid
9511      */
9512     markValid : function()
9513     {
9514         if(!this.el  || this.preventMark){ // not rendered...
9515             return;
9516         }
9517         
9518         this.el.removeClass([this.invalidClass, this.validClass]);
9519         
9520         var feedback = this.el.select('.form-control-feedback', true).first();
9521             
9522         if(feedback){
9523             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9524         }
9525         
9526         if(this.indicator){
9527             this.indicator.removeClass('visible');
9528             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9529         }
9530         
9531         if(this.disabled){
9532             return;
9533         }
9534         
9535         if(this.allowBlank && !this.getRawValue().length){
9536             return;
9537         }
9538         
9539         this.el.addClass(this.validClass);
9540         
9541         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9542             
9543             var feedback = this.el.select('.form-control-feedback', true).first();
9544             
9545             if(feedback){
9546                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9547                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9548             }
9549             
9550         }
9551         
9552         this.fireEvent('valid', this);
9553     },
9554     
9555      /**
9556      * Mark this field as invalid
9557      * @param {String} msg The validation message
9558      */
9559     markInvalid : function(msg)
9560     {
9561         if(!this.el  || this.preventMark){ // not rendered
9562             return;
9563         }
9564         
9565         this.el.removeClass([this.invalidClass, this.validClass]);
9566         
9567         var feedback = this.el.select('.form-control-feedback', true).first();
9568             
9569         if(feedback){
9570             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9571         }
9572
9573         if(this.disabled){
9574             return;
9575         }
9576         
9577         if(this.allowBlank && !this.getRawValue().length){
9578             return;
9579         }
9580         
9581         if(this.indicator){
9582             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9583             this.indicator.addClass('visible');
9584         }
9585         
9586         this.el.addClass(this.invalidClass);
9587         
9588         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9589             
9590             var feedback = this.el.select('.form-control-feedback', true).first();
9591             
9592             if(feedback){
9593                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9594                 
9595                 if(this.getValue().length || this.forceFeedback){
9596                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9597                 }
9598                 
9599             }
9600             
9601         }
9602         
9603         this.fireEvent('invalid', this, msg);
9604     },
9605     // private
9606     SafariOnKeyDown : function(event)
9607     {
9608         // this is a workaround for a password hang bug on chrome/ webkit.
9609         if (this.inputEl().dom.type != 'password') {
9610             return;
9611         }
9612         
9613         var isSelectAll = false;
9614         
9615         if(this.inputEl().dom.selectionEnd > 0){
9616             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9617         }
9618         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9619             event.preventDefault();
9620             this.setValue('');
9621             return;
9622         }
9623         
9624         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9625             
9626             event.preventDefault();
9627             // this is very hacky as keydown always get's upper case.
9628             //
9629             var cc = String.fromCharCode(event.getCharCode());
9630             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9631             
9632         }
9633     },
9634     adjustWidth : function(tag, w){
9635         tag = tag.toLowerCase();
9636         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9637             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9638                 if(tag == 'input'){
9639                     return w + 2;
9640                 }
9641                 if(tag == 'textarea'){
9642                     return w-2;
9643                 }
9644             }else if(Roo.isOpera){
9645                 if(tag == 'input'){
9646                     return w + 2;
9647                 }
9648                 if(tag == 'textarea'){
9649                     return w-2;
9650                 }
9651             }
9652         }
9653         return w;
9654     },
9655     
9656     setFieldLabel : function(v)
9657     {
9658         if(!this.rendered){
9659             return;
9660         }
9661         
9662         if(this.indicator){
9663             var ar = this.el.select('label > span',true);
9664             
9665             if (ar.elements.length) {
9666                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9667                 this.fieldLabel = v;
9668                 return;
9669             }
9670             
9671             var br = this.el.select('label',true);
9672             
9673             if(br.elements.length) {
9674                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9675                 this.fieldLabel = v;
9676                 return;
9677             }
9678             
9679             Roo.log('Cannot Found any of label > span || label in input');
9680             return;
9681         }
9682         
9683         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9684         this.fieldLabel = v;
9685         
9686         
9687     }
9688 });
9689
9690  
9691 /*
9692  * - LGPL
9693  *
9694  * Input
9695  * 
9696  */
9697
9698 /**
9699  * @class Roo.bootstrap.TextArea
9700  * @extends Roo.bootstrap.Input
9701  * Bootstrap TextArea class
9702  * @cfg {Number} cols Specifies the visible width of a text area
9703  * @cfg {Number} rows Specifies the visible number of lines in a text area
9704  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9705  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9706  * @cfg {string} html text
9707  * 
9708  * @constructor
9709  * Create a new TextArea
9710  * @param {Object} config The config object
9711  */
9712
9713 Roo.bootstrap.TextArea = function(config){
9714     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9715    
9716 };
9717
9718 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9719      
9720     cols : false,
9721     rows : 5,
9722     readOnly : false,
9723     warp : 'soft',
9724     resize : false,
9725     value: false,
9726     html: false,
9727     
9728     getAutoCreate : function(){
9729         
9730         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9731         
9732         var id = Roo.id();
9733         
9734         var cfg = {};
9735         
9736         if(this.inputType != 'hidden'){
9737             cfg.cls = 'form-group' //input-group
9738         }
9739         
9740         var input =  {
9741             tag: 'textarea',
9742             id : id,
9743             warp : this.warp,
9744             rows : this.rows,
9745             value : this.value || '',
9746             html: this.html || '',
9747             cls : 'form-control',
9748             placeholder : this.placeholder || '' 
9749             
9750         };
9751         
9752         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9753             input.maxLength = this.maxLength;
9754         }
9755         
9756         if(this.resize){
9757             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9758         }
9759         
9760         if(this.cols){
9761             input.cols = this.cols;
9762         }
9763         
9764         if (this.readOnly) {
9765             input.readonly = true;
9766         }
9767         
9768         if (this.name) {
9769             input.name = this.name;
9770         }
9771         
9772         if (this.size) {
9773             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9774         }
9775         
9776         var settings=this;
9777         ['xs','sm','md','lg'].map(function(size){
9778             if (settings[size]) {
9779                 cfg.cls += ' col-' + size + '-' + settings[size];
9780             }
9781         });
9782         
9783         var inputblock = input;
9784         
9785         if(this.hasFeedback && !this.allowBlank){
9786             
9787             var feedback = {
9788                 tag: 'span',
9789                 cls: 'glyphicon form-control-feedback'
9790             };
9791
9792             inputblock = {
9793                 cls : 'has-feedback',
9794                 cn :  [
9795                     input,
9796                     feedback
9797                 ] 
9798             };  
9799         }
9800         
9801         
9802         if (this.before || this.after) {
9803             
9804             inputblock = {
9805                 cls : 'input-group',
9806                 cn :  [] 
9807             };
9808             if (this.before) {
9809                 inputblock.cn.push({
9810                     tag :'span',
9811                     cls : 'input-group-addon',
9812                     html : this.before
9813                 });
9814             }
9815             
9816             inputblock.cn.push(input);
9817             
9818             if(this.hasFeedback && !this.allowBlank){
9819                 inputblock.cls += ' has-feedback';
9820                 inputblock.cn.push(feedback);
9821             }
9822             
9823             if (this.after) {
9824                 inputblock.cn.push({
9825                     tag :'span',
9826                     cls : 'input-group-addon',
9827                     html : this.after
9828                 });
9829             }
9830             
9831         }
9832         
9833         if (align ==='left' && this.fieldLabel.length) {
9834             cfg.cn = [
9835                 {
9836                     tag: 'label',
9837                     'for' :  id,
9838                     cls : 'control-label',
9839                     html : this.fieldLabel
9840                 },
9841                 {
9842                     cls : "",
9843                     cn: [
9844                         inputblock
9845                     ]
9846                 }
9847
9848             ];
9849             
9850             if(this.labelWidth > 12){
9851                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9852             }
9853
9854             if(this.labelWidth < 13 && this.labelmd == 0){
9855                 this.labelmd = this.labelWidth;
9856             }
9857
9858             if(this.labellg > 0){
9859                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9860                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9861             }
9862
9863             if(this.labelmd > 0){
9864                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9865                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9866             }
9867
9868             if(this.labelsm > 0){
9869                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9870                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9871             }
9872
9873             if(this.labelxs > 0){
9874                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9875                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9876             }
9877             
9878         } else if ( this.fieldLabel.length) {
9879             cfg.cn = [
9880
9881                {
9882                    tag: 'label',
9883                    //cls : 'input-group-addon',
9884                    html : this.fieldLabel
9885
9886                },
9887
9888                inputblock
9889
9890            ];
9891
9892         } else {
9893
9894             cfg.cn = [
9895
9896                 inputblock
9897
9898             ];
9899                 
9900         }
9901         
9902         if (this.disabled) {
9903             input.disabled=true;
9904         }
9905         
9906         return cfg;
9907         
9908     },
9909     /**
9910      * return the real textarea element.
9911      */
9912     inputEl: function ()
9913     {
9914         return this.el.select('textarea.form-control',true).first();
9915     },
9916     
9917     /**
9918      * Clear any invalid styles/messages for this field
9919      */
9920     clearInvalid : function()
9921     {
9922         
9923         if(!this.el || this.preventMark){ // not rendered
9924             return;
9925         }
9926         
9927         var label = this.el.select('label', true).first();
9928         var icon = this.el.select('i.fa-star', true).first();
9929         
9930         if(label && icon){
9931             icon.remove();
9932         }
9933         
9934         this.el.removeClass(this.invalidClass);
9935         
9936         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9937             
9938             var feedback = this.el.select('.form-control-feedback', true).first();
9939             
9940             if(feedback){
9941                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9942             }
9943             
9944         }
9945         
9946         this.fireEvent('valid', this);
9947     },
9948     
9949      /**
9950      * Mark this field as valid
9951      */
9952     markValid : function()
9953     {
9954         if(!this.el  || this.preventMark){ // not rendered
9955             return;
9956         }
9957         
9958         this.el.removeClass([this.invalidClass, this.validClass]);
9959         
9960         var feedback = this.el.select('.form-control-feedback', true).first();
9961             
9962         if(feedback){
9963             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9964         }
9965
9966         if(this.disabled || this.allowBlank){
9967             return;
9968         }
9969         
9970         var label = this.el.select('label', true).first();
9971         var icon = this.el.select('i.fa-star', true).first();
9972         
9973         if(label && icon){
9974             icon.remove();
9975         }
9976         
9977         this.el.addClass(this.validClass);
9978         
9979         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9980             
9981             var feedback = this.el.select('.form-control-feedback', true).first();
9982             
9983             if(feedback){
9984                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9985                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9986             }
9987             
9988         }
9989         
9990         this.fireEvent('valid', this);
9991     },
9992     
9993      /**
9994      * Mark this field as invalid
9995      * @param {String} msg The validation message
9996      */
9997     markInvalid : function(msg)
9998     {
9999         if(!this.el  || this.preventMark){ // not rendered
10000             return;
10001         }
10002         
10003         this.el.removeClass([this.invalidClass, this.validClass]);
10004         
10005         var feedback = this.el.select('.form-control-feedback', true).first();
10006             
10007         if(feedback){
10008             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10009         }
10010
10011         if(this.disabled || this.allowBlank){
10012             return;
10013         }
10014         
10015         var label = this.el.select('label', true).first();
10016         var icon = this.el.select('i.fa-star', true).first();
10017         
10018         if(!this.getValue().length && label && !icon){
10019             this.el.createChild({
10020                 tag : 'i',
10021                 cls : 'text-danger fa fa-lg fa-star',
10022                 tooltip : 'This field is required',
10023                 style : 'margin-right:5px;'
10024             }, label, true);
10025         }
10026
10027         this.el.addClass(this.invalidClass);
10028         
10029         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10030             
10031             var feedback = this.el.select('.form-control-feedback', true).first();
10032             
10033             if(feedback){
10034                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10035                 
10036                 if(this.getValue().length || this.forceFeedback){
10037                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10038                 }
10039                 
10040             }
10041             
10042         }
10043         
10044         this.fireEvent('invalid', this, msg);
10045     }
10046 });
10047
10048  
10049 /*
10050  * - LGPL
10051  *
10052  * trigger field - base class for combo..
10053  * 
10054  */
10055  
10056 /**
10057  * @class Roo.bootstrap.TriggerField
10058  * @extends Roo.bootstrap.Input
10059  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10060  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10061  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10062  * for which you can provide a custom implementation.  For example:
10063  * <pre><code>
10064 var trigger = new Roo.bootstrap.TriggerField();
10065 trigger.onTriggerClick = myTriggerFn;
10066 trigger.applyTo('my-field');
10067 </code></pre>
10068  *
10069  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10070  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10071  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10072  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10073  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10074
10075  * @constructor
10076  * Create a new TriggerField.
10077  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10078  * to the base TextField)
10079  */
10080 Roo.bootstrap.TriggerField = function(config){
10081     this.mimicing = false;
10082     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10083 };
10084
10085 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10086     /**
10087      * @cfg {String} triggerClass A CSS class to apply to the trigger
10088      */
10089      /**
10090      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10091      */
10092     hideTrigger:false,
10093
10094     /**
10095      * @cfg {Boolean} removable (true|false) special filter default false
10096      */
10097     removable : false,
10098     
10099     /** @cfg {Boolean} grow @hide */
10100     /** @cfg {Number} growMin @hide */
10101     /** @cfg {Number} growMax @hide */
10102
10103     /**
10104      * @hide 
10105      * @method
10106      */
10107     autoSize: Roo.emptyFn,
10108     // private
10109     monitorTab : true,
10110     // private
10111     deferHeight : true,
10112
10113     
10114     actionMode : 'wrap',
10115     
10116     caret : false,
10117     
10118     
10119     getAutoCreate : function(){
10120        
10121         var align = this.labelAlign || this.parentLabelAlign();
10122         
10123         var id = Roo.id();
10124         
10125         var cfg = {
10126             cls: 'form-group' //input-group
10127         };
10128         
10129         
10130         var input =  {
10131             tag: 'input',
10132             id : id,
10133             type : this.inputType,
10134             cls : 'form-control',
10135             autocomplete: 'new-password',
10136             placeholder : this.placeholder || '' 
10137             
10138         };
10139         if (this.name) {
10140             input.name = this.name;
10141         }
10142         if (this.size) {
10143             input.cls += ' input-' + this.size;
10144         }
10145         
10146         if (this.disabled) {
10147             input.disabled=true;
10148         }
10149         
10150         var inputblock = input;
10151         
10152         if(this.hasFeedback && !this.allowBlank){
10153             
10154             var feedback = {
10155                 tag: 'span',
10156                 cls: 'glyphicon form-control-feedback'
10157             };
10158             
10159             if(this.removable && !this.editable && !this.tickable){
10160                 inputblock = {
10161                     cls : 'has-feedback',
10162                     cn :  [
10163                         inputblock,
10164                         {
10165                             tag: 'button',
10166                             html : 'x',
10167                             cls : 'roo-combo-removable-btn close'
10168                         },
10169                         feedback
10170                     ] 
10171                 };
10172             } else {
10173                 inputblock = {
10174                     cls : 'has-feedback',
10175                     cn :  [
10176                         inputblock,
10177                         feedback
10178                     ] 
10179                 };
10180             }
10181
10182         } else {
10183             if(this.removable && !this.editable && !this.tickable){
10184                 inputblock = {
10185                     cls : 'roo-removable',
10186                     cn :  [
10187                         inputblock,
10188                         {
10189                             tag: 'button',
10190                             html : 'x',
10191                             cls : 'roo-combo-removable-btn close'
10192                         }
10193                     ] 
10194                 };
10195             }
10196         }
10197         
10198         if (this.before || this.after) {
10199             
10200             inputblock = {
10201                 cls : 'input-group',
10202                 cn :  [] 
10203             };
10204             if (this.before) {
10205                 inputblock.cn.push({
10206                     tag :'span',
10207                     cls : 'input-group-addon',
10208                     html : this.before
10209                 });
10210             }
10211             
10212             inputblock.cn.push(input);
10213             
10214             if(this.hasFeedback && !this.allowBlank){
10215                 inputblock.cls += ' has-feedback';
10216                 inputblock.cn.push(feedback);
10217             }
10218             
10219             if (this.after) {
10220                 inputblock.cn.push({
10221                     tag :'span',
10222                     cls : 'input-group-addon',
10223                     html : this.after
10224                 });
10225             }
10226             
10227         };
10228         
10229         var box = {
10230             tag: 'div',
10231             cn: [
10232                 {
10233                     tag: 'input',
10234                     type : 'hidden',
10235                     cls: 'form-hidden-field'
10236                 },
10237                 inputblock
10238             ]
10239             
10240         };
10241         
10242         if(this.multiple){
10243             box = {
10244                 tag: 'div',
10245                 cn: [
10246                     {
10247                         tag: 'input',
10248                         type : 'hidden',
10249                         cls: 'form-hidden-field'
10250                     },
10251                     {
10252                         tag: 'ul',
10253                         cls: 'roo-select2-choices',
10254                         cn:[
10255                             {
10256                                 tag: 'li',
10257                                 cls: 'roo-select2-search-field',
10258                                 cn: [
10259
10260                                     inputblock
10261                                 ]
10262                             }
10263                         ]
10264                     }
10265                 ]
10266             }
10267         };
10268         
10269         var combobox = {
10270             cls: 'roo-select2-container input-group',
10271             cn: [
10272                 box
10273 //                {
10274 //                    tag: 'ul',
10275 //                    cls: 'typeahead typeahead-long dropdown-menu',
10276 //                    style: 'display:none'
10277 //                }
10278             ]
10279         };
10280         
10281         if(!this.multiple && this.showToggleBtn){
10282             
10283             var caret = {
10284                         tag: 'span',
10285                         cls: 'caret'
10286              };
10287             if (this.caret != false) {
10288                 caret = {
10289                      tag: 'i',
10290                      cls: 'fa fa-' + this.caret
10291                 };
10292                 
10293             }
10294             
10295             combobox.cn.push({
10296                 tag :'span',
10297                 cls : 'input-group-addon btn dropdown-toggle',
10298                 cn : [
10299                     caret,
10300                     {
10301                         tag: 'span',
10302                         cls: 'combobox-clear',
10303                         cn  : [
10304                             {
10305                                 tag : 'i',
10306                                 cls: 'icon-remove'
10307                             }
10308                         ]
10309                     }
10310                 ]
10311
10312             })
10313         }
10314         
10315         if(this.multiple){
10316             combobox.cls += ' roo-select2-container-multi';
10317         }
10318         
10319         if (align ==='left' && this.fieldLabel.length) {
10320             
10321             cfg.cls += ' roo-form-group-label-left';
10322
10323             cfg.cn = [
10324                 {
10325                     tag : 'i',
10326                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10327                     tooltip : 'This field is required'
10328                 },
10329                 {
10330                     tag: 'label',
10331                     'for' :  id,
10332                     cls : 'control-label',
10333                     html : this.fieldLabel
10334
10335                 },
10336                 {
10337                     cls : "", 
10338                     cn: [
10339                         combobox
10340                     ]
10341                 }
10342
10343             ];
10344             
10345             var labelCfg = cfg.cn[1];
10346             var contentCfg = cfg.cn[2];
10347             
10348             if(this.indicatorpos == 'right'){
10349                 cfg.cn = [
10350                     {
10351                         tag: 'label',
10352                         'for' :  id,
10353                         cls : 'control-label',
10354                         cn : [
10355                             {
10356                                 tag : 'span',
10357                                 html : this.fieldLabel
10358                             },
10359                             {
10360                                 tag : 'i',
10361                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10362                                 tooltip : 'This field is required'
10363                             }
10364                         ]
10365                     },
10366                     {
10367                         cls : "", 
10368                         cn: [
10369                             combobox
10370                         ]
10371                     }
10372
10373                 ];
10374                 
10375                 labelCfg = cfg.cn[0];
10376                 contentCfg = cfg.cn[1];
10377             }
10378             
10379             if(this.labelWidth > 12){
10380                 labelCfg.style = "width: " + this.labelWidth + 'px';
10381             }
10382             
10383             if(this.labelWidth < 13 && this.labelmd == 0){
10384                 this.labelmd = this.labelWidth;
10385             }
10386             
10387             if(this.labellg > 0){
10388                 labelCfg.cls += ' col-lg-' + this.labellg;
10389                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10390             }
10391             
10392             if(this.labelmd > 0){
10393                 labelCfg.cls += ' col-md-' + this.labelmd;
10394                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10395             }
10396             
10397             if(this.labelsm > 0){
10398                 labelCfg.cls += ' col-sm-' + this.labelsm;
10399                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10400             }
10401             
10402             if(this.labelxs > 0){
10403                 labelCfg.cls += ' col-xs-' + this.labelxs;
10404                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10405             }
10406             
10407         } else if ( this.fieldLabel.length) {
10408 //                Roo.log(" label");
10409             cfg.cn = [
10410                 {
10411                    tag : 'i',
10412                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10413                    tooltip : 'This field is required'
10414                },
10415                {
10416                    tag: 'label',
10417                    //cls : 'input-group-addon',
10418                    html : this.fieldLabel
10419
10420                },
10421
10422                combobox
10423
10424             ];
10425             
10426             if(this.indicatorpos == 'right'){
10427                 
10428                 cfg.cn = [
10429                     {
10430                        tag: 'label',
10431                        cn : [
10432                            {
10433                                tag : 'span',
10434                                html : this.fieldLabel
10435                            },
10436                            {
10437                               tag : 'i',
10438                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10439                               tooltip : 'This field is required'
10440                            }
10441                        ]
10442
10443                     },
10444                     combobox
10445
10446                 ];
10447
10448             }
10449
10450         } else {
10451             
10452 //                Roo.log(" no label && no align");
10453                 cfg = combobox
10454                      
10455                 
10456         }
10457         
10458         var settings=this;
10459         ['xs','sm','md','lg'].map(function(size){
10460             if (settings[size]) {
10461                 cfg.cls += ' col-' + size + '-' + settings[size];
10462             }
10463         });
10464         
10465         return cfg;
10466         
10467     },
10468     
10469     
10470     
10471     // private
10472     onResize : function(w, h){
10473 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10474 //        if(typeof w == 'number'){
10475 //            var x = w - this.trigger.getWidth();
10476 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10477 //            this.trigger.setStyle('left', x+'px');
10478 //        }
10479     },
10480
10481     // private
10482     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10483
10484     // private
10485     getResizeEl : function(){
10486         return this.inputEl();
10487     },
10488
10489     // private
10490     getPositionEl : function(){
10491         return this.inputEl();
10492     },
10493
10494     // private
10495     alignErrorIcon : function(){
10496         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10497     },
10498
10499     // private
10500     initEvents : function(){
10501         
10502         this.createList();
10503         
10504         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10505         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10506         if(!this.multiple && this.showToggleBtn){
10507             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10508             if(this.hideTrigger){
10509                 this.trigger.setDisplayed(false);
10510             }
10511             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10512         }
10513         
10514         if(this.multiple){
10515             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10516         }
10517         
10518         if(this.removable && !this.editable && !this.tickable){
10519             var close = this.closeTriggerEl();
10520             
10521             if(close){
10522                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10523                 close.on('click', this.removeBtnClick, this, close);
10524             }
10525         }
10526         
10527         //this.trigger.addClassOnOver('x-form-trigger-over');
10528         //this.trigger.addClassOnClick('x-form-trigger-click');
10529         
10530         //if(!this.width){
10531         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10532         //}
10533     },
10534     
10535     closeTriggerEl : function()
10536     {
10537         var close = this.el.select('.roo-combo-removable-btn', true).first();
10538         return close ? close : false;
10539     },
10540     
10541     removeBtnClick : function(e, h, el)
10542     {
10543         e.preventDefault();
10544         
10545         if(this.fireEvent("remove", this) !== false){
10546             this.reset();
10547             this.fireEvent("afterremove", this)
10548         }
10549     },
10550     
10551     createList : function()
10552     {
10553         this.list = Roo.get(document.body).createChild({
10554             tag: 'ul',
10555             cls: 'typeahead typeahead-long dropdown-menu',
10556             style: 'display:none'
10557         });
10558         
10559         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10560         
10561     },
10562
10563     // private
10564     initTrigger : function(){
10565        
10566     },
10567
10568     // private
10569     onDestroy : function(){
10570         if(this.trigger){
10571             this.trigger.removeAllListeners();
10572           //  this.trigger.remove();
10573         }
10574         //if(this.wrap){
10575         //    this.wrap.remove();
10576         //}
10577         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10578     },
10579
10580     // private
10581     onFocus : function(){
10582         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10583         /*
10584         if(!this.mimicing){
10585             this.wrap.addClass('x-trigger-wrap-focus');
10586             this.mimicing = true;
10587             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10588             if(this.monitorTab){
10589                 this.el.on("keydown", this.checkTab, this);
10590             }
10591         }
10592         */
10593     },
10594
10595     // private
10596     checkTab : function(e){
10597         if(e.getKey() == e.TAB){
10598             this.triggerBlur();
10599         }
10600     },
10601
10602     // private
10603     onBlur : function(){
10604         // do nothing
10605     },
10606
10607     // private
10608     mimicBlur : function(e, t){
10609         /*
10610         if(!this.wrap.contains(t) && this.validateBlur()){
10611             this.triggerBlur();
10612         }
10613         */
10614     },
10615
10616     // private
10617     triggerBlur : function(){
10618         this.mimicing = false;
10619         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10620         if(this.monitorTab){
10621             this.el.un("keydown", this.checkTab, this);
10622         }
10623         //this.wrap.removeClass('x-trigger-wrap-focus');
10624         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10625     },
10626
10627     // private
10628     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10629     validateBlur : function(e, t){
10630         return true;
10631     },
10632
10633     // private
10634     onDisable : function(){
10635         this.inputEl().dom.disabled = true;
10636         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10637         //if(this.wrap){
10638         //    this.wrap.addClass('x-item-disabled');
10639         //}
10640     },
10641
10642     // private
10643     onEnable : function(){
10644         this.inputEl().dom.disabled = false;
10645         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10646         //if(this.wrap){
10647         //    this.el.removeClass('x-item-disabled');
10648         //}
10649     },
10650
10651     // private
10652     onShow : function(){
10653         var ae = this.getActionEl();
10654         
10655         if(ae){
10656             ae.dom.style.display = '';
10657             ae.dom.style.visibility = 'visible';
10658         }
10659     },
10660
10661     // private
10662     
10663     onHide : function(){
10664         var ae = this.getActionEl();
10665         ae.dom.style.display = 'none';
10666     },
10667
10668     /**
10669      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10670      * by an implementing function.
10671      * @method
10672      * @param {EventObject} e
10673      */
10674     onTriggerClick : Roo.emptyFn
10675 });
10676  /*
10677  * Based on:
10678  * Ext JS Library 1.1.1
10679  * Copyright(c) 2006-2007, Ext JS, LLC.
10680  *
10681  * Originally Released Under LGPL - original licence link has changed is not relivant.
10682  *
10683  * Fork - LGPL
10684  * <script type="text/javascript">
10685  */
10686
10687
10688 /**
10689  * @class Roo.data.SortTypes
10690  * @singleton
10691  * Defines the default sorting (casting?) comparison functions used when sorting data.
10692  */
10693 Roo.data.SortTypes = {
10694     /**
10695      * Default sort that does nothing
10696      * @param {Mixed} s The value being converted
10697      * @return {Mixed} The comparison value
10698      */
10699     none : function(s){
10700         return s;
10701     },
10702     
10703     /**
10704      * The regular expression used to strip tags
10705      * @type {RegExp}
10706      * @property
10707      */
10708     stripTagsRE : /<\/?[^>]+>/gi,
10709     
10710     /**
10711      * Strips all HTML tags to sort on text only
10712      * @param {Mixed} s The value being converted
10713      * @return {String} The comparison value
10714      */
10715     asText : function(s){
10716         return String(s).replace(this.stripTagsRE, "");
10717     },
10718     
10719     /**
10720      * Strips all HTML tags to sort on text only - Case insensitive
10721      * @param {Mixed} s The value being converted
10722      * @return {String} The comparison value
10723      */
10724     asUCText : function(s){
10725         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10726     },
10727     
10728     /**
10729      * Case insensitive string
10730      * @param {Mixed} s The value being converted
10731      * @return {String} The comparison value
10732      */
10733     asUCString : function(s) {
10734         return String(s).toUpperCase();
10735     },
10736     
10737     /**
10738      * Date sorting
10739      * @param {Mixed} s The value being converted
10740      * @return {Number} The comparison value
10741      */
10742     asDate : function(s) {
10743         if(!s){
10744             return 0;
10745         }
10746         if(s instanceof Date){
10747             return s.getTime();
10748         }
10749         return Date.parse(String(s));
10750     },
10751     
10752     /**
10753      * Float sorting
10754      * @param {Mixed} s The value being converted
10755      * @return {Float} The comparison value
10756      */
10757     asFloat : function(s) {
10758         var val = parseFloat(String(s).replace(/,/g, ""));
10759         if(isNaN(val)) {
10760             val = 0;
10761         }
10762         return val;
10763     },
10764     
10765     /**
10766      * Integer sorting
10767      * @param {Mixed} s The value being converted
10768      * @return {Number} The comparison value
10769      */
10770     asInt : function(s) {
10771         var val = parseInt(String(s).replace(/,/g, ""));
10772         if(isNaN(val)) {
10773             val = 0;
10774         }
10775         return val;
10776     }
10777 };/*
10778  * Based on:
10779  * Ext JS Library 1.1.1
10780  * Copyright(c) 2006-2007, Ext JS, LLC.
10781  *
10782  * Originally Released Under LGPL - original licence link has changed is not relivant.
10783  *
10784  * Fork - LGPL
10785  * <script type="text/javascript">
10786  */
10787
10788 /**
10789 * @class Roo.data.Record
10790  * Instances of this class encapsulate both record <em>definition</em> information, and record
10791  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10792  * to access Records cached in an {@link Roo.data.Store} object.<br>
10793  * <p>
10794  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10795  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10796  * objects.<br>
10797  * <p>
10798  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10799  * @constructor
10800  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10801  * {@link #create}. The parameters are the same.
10802  * @param {Array} data An associative Array of data values keyed by the field name.
10803  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10804  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10805  * not specified an integer id is generated.
10806  */
10807 Roo.data.Record = function(data, id){
10808     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10809     this.data = data;
10810 };
10811
10812 /**
10813  * Generate a constructor for a specific record layout.
10814  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10815  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10816  * Each field definition object may contain the following properties: <ul>
10817  * <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,
10818  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10819  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10820  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10821  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10822  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10823  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10824  * this may be omitted.</p></li>
10825  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10826  * <ul><li>auto (Default, implies no conversion)</li>
10827  * <li>string</li>
10828  * <li>int</li>
10829  * <li>float</li>
10830  * <li>boolean</li>
10831  * <li>date</li></ul></p></li>
10832  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10833  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10834  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10835  * by the Reader into an object that will be stored in the Record. It is passed the
10836  * following parameters:<ul>
10837  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10838  * </ul></p></li>
10839  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10840  * </ul>
10841  * <br>usage:<br><pre><code>
10842 var TopicRecord = Roo.data.Record.create(
10843     {name: 'title', mapping: 'topic_title'},
10844     {name: 'author', mapping: 'username'},
10845     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10846     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10847     {name: 'lastPoster', mapping: 'user2'},
10848     {name: 'excerpt', mapping: 'post_text'}
10849 );
10850
10851 var myNewRecord = new TopicRecord({
10852     title: 'Do my job please',
10853     author: 'noobie',
10854     totalPosts: 1,
10855     lastPost: new Date(),
10856     lastPoster: 'Animal',
10857     excerpt: 'No way dude!'
10858 });
10859 myStore.add(myNewRecord);
10860 </code></pre>
10861  * @method create
10862  * @static
10863  */
10864 Roo.data.Record.create = function(o){
10865     var f = function(){
10866         f.superclass.constructor.apply(this, arguments);
10867     };
10868     Roo.extend(f, Roo.data.Record);
10869     var p = f.prototype;
10870     p.fields = new Roo.util.MixedCollection(false, function(field){
10871         return field.name;
10872     });
10873     for(var i = 0, len = o.length; i < len; i++){
10874         p.fields.add(new Roo.data.Field(o[i]));
10875     }
10876     f.getField = function(name){
10877         return p.fields.get(name);  
10878     };
10879     return f;
10880 };
10881
10882 Roo.data.Record.AUTO_ID = 1000;
10883 Roo.data.Record.EDIT = 'edit';
10884 Roo.data.Record.REJECT = 'reject';
10885 Roo.data.Record.COMMIT = 'commit';
10886
10887 Roo.data.Record.prototype = {
10888     /**
10889      * Readonly flag - true if this record has been modified.
10890      * @type Boolean
10891      */
10892     dirty : false,
10893     editing : false,
10894     error: null,
10895     modified: null,
10896
10897     // private
10898     join : function(store){
10899         this.store = store;
10900     },
10901
10902     /**
10903      * Set the named field to the specified value.
10904      * @param {String} name The name of the field to set.
10905      * @param {Object} value The value to set the field to.
10906      */
10907     set : function(name, value){
10908         if(this.data[name] == value){
10909             return;
10910         }
10911         this.dirty = true;
10912         if(!this.modified){
10913             this.modified = {};
10914         }
10915         if(typeof this.modified[name] == 'undefined'){
10916             this.modified[name] = this.data[name];
10917         }
10918         this.data[name] = value;
10919         if(!this.editing && this.store){
10920             this.store.afterEdit(this);
10921         }       
10922     },
10923
10924     /**
10925      * Get the value of the named field.
10926      * @param {String} name The name of the field to get the value of.
10927      * @return {Object} The value of the field.
10928      */
10929     get : function(name){
10930         return this.data[name]; 
10931     },
10932
10933     // private
10934     beginEdit : function(){
10935         this.editing = true;
10936         this.modified = {}; 
10937     },
10938
10939     // private
10940     cancelEdit : function(){
10941         this.editing = false;
10942         delete this.modified;
10943     },
10944
10945     // private
10946     endEdit : function(){
10947         this.editing = false;
10948         if(this.dirty && this.store){
10949             this.store.afterEdit(this);
10950         }
10951     },
10952
10953     /**
10954      * Usually called by the {@link Roo.data.Store} which owns the Record.
10955      * Rejects all changes made to the Record since either creation, or the last commit operation.
10956      * Modified fields are reverted to their original values.
10957      * <p>
10958      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10959      * of reject operations.
10960      */
10961     reject : function(){
10962         var m = this.modified;
10963         for(var n in m){
10964             if(typeof m[n] != "function"){
10965                 this.data[n] = m[n];
10966             }
10967         }
10968         this.dirty = false;
10969         delete this.modified;
10970         this.editing = false;
10971         if(this.store){
10972             this.store.afterReject(this);
10973         }
10974     },
10975
10976     /**
10977      * Usually called by the {@link Roo.data.Store} which owns the Record.
10978      * Commits all changes made to the Record since either creation, or the last commit operation.
10979      * <p>
10980      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10981      * of commit operations.
10982      */
10983     commit : function(){
10984         this.dirty = false;
10985         delete this.modified;
10986         this.editing = false;
10987         if(this.store){
10988             this.store.afterCommit(this);
10989         }
10990     },
10991
10992     // private
10993     hasError : function(){
10994         return this.error != null;
10995     },
10996
10997     // private
10998     clearError : function(){
10999         this.error = null;
11000     },
11001
11002     /**
11003      * Creates a copy of this record.
11004      * @param {String} id (optional) A new record id if you don't want to use this record's id
11005      * @return {Record}
11006      */
11007     copy : function(newId) {
11008         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11009     }
11010 };/*
11011  * Based on:
11012  * Ext JS Library 1.1.1
11013  * Copyright(c) 2006-2007, Ext JS, LLC.
11014  *
11015  * Originally Released Under LGPL - original licence link has changed is not relivant.
11016  *
11017  * Fork - LGPL
11018  * <script type="text/javascript">
11019  */
11020
11021
11022
11023 /**
11024  * @class Roo.data.Store
11025  * @extends Roo.util.Observable
11026  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11027  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11028  * <p>
11029  * 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
11030  * has no knowledge of the format of the data returned by the Proxy.<br>
11031  * <p>
11032  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11033  * instances from the data object. These records are cached and made available through accessor functions.
11034  * @constructor
11035  * Creates a new Store.
11036  * @param {Object} config A config object containing the objects needed for the Store to access data,
11037  * and read the data into Records.
11038  */
11039 Roo.data.Store = function(config){
11040     this.data = new Roo.util.MixedCollection(false);
11041     this.data.getKey = function(o){
11042         return o.id;
11043     };
11044     this.baseParams = {};
11045     // private
11046     this.paramNames = {
11047         "start" : "start",
11048         "limit" : "limit",
11049         "sort" : "sort",
11050         "dir" : "dir",
11051         "multisort" : "_multisort"
11052     };
11053
11054     if(config && config.data){
11055         this.inlineData = config.data;
11056         delete config.data;
11057     }
11058
11059     Roo.apply(this, config);
11060     
11061     if(this.reader){ // reader passed
11062         this.reader = Roo.factory(this.reader, Roo.data);
11063         this.reader.xmodule = this.xmodule || false;
11064         if(!this.recordType){
11065             this.recordType = this.reader.recordType;
11066         }
11067         if(this.reader.onMetaChange){
11068             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11069         }
11070     }
11071
11072     if(this.recordType){
11073         this.fields = this.recordType.prototype.fields;
11074     }
11075     this.modified = [];
11076
11077     this.addEvents({
11078         /**
11079          * @event datachanged
11080          * Fires when the data cache has changed, and a widget which is using this Store
11081          * as a Record cache should refresh its view.
11082          * @param {Store} this
11083          */
11084         datachanged : true,
11085         /**
11086          * @event metachange
11087          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11088          * @param {Store} this
11089          * @param {Object} meta The JSON metadata
11090          */
11091         metachange : true,
11092         /**
11093          * @event add
11094          * Fires when Records have been added to the Store
11095          * @param {Store} this
11096          * @param {Roo.data.Record[]} records The array of Records added
11097          * @param {Number} index The index at which the record(s) were added
11098          */
11099         add : true,
11100         /**
11101          * @event remove
11102          * Fires when a Record has been removed from the Store
11103          * @param {Store} this
11104          * @param {Roo.data.Record} record The Record that was removed
11105          * @param {Number} index The index at which the record was removed
11106          */
11107         remove : true,
11108         /**
11109          * @event update
11110          * Fires when a Record has been updated
11111          * @param {Store} this
11112          * @param {Roo.data.Record} record The Record that was updated
11113          * @param {String} operation The update operation being performed.  Value may be one of:
11114          * <pre><code>
11115  Roo.data.Record.EDIT
11116  Roo.data.Record.REJECT
11117  Roo.data.Record.COMMIT
11118          * </code></pre>
11119          */
11120         update : true,
11121         /**
11122          * @event clear
11123          * Fires when the data cache has been cleared.
11124          * @param {Store} this
11125          */
11126         clear : true,
11127         /**
11128          * @event beforeload
11129          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11130          * the load action will be canceled.
11131          * @param {Store} this
11132          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11133          */
11134         beforeload : true,
11135         /**
11136          * @event beforeloadadd
11137          * Fires after a new set of Records has been loaded.
11138          * @param {Store} this
11139          * @param {Roo.data.Record[]} records The Records that were loaded
11140          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11141          */
11142         beforeloadadd : true,
11143         /**
11144          * @event load
11145          * Fires after a new set of Records has been loaded, before they are added to the store.
11146          * @param {Store} this
11147          * @param {Roo.data.Record[]} records The Records that were loaded
11148          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11149          * @params {Object} return from reader
11150          */
11151         load : true,
11152         /**
11153          * @event loadexception
11154          * Fires if an exception occurs in the Proxy during loading.
11155          * Called with the signature of the Proxy's "loadexception" event.
11156          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11157          * 
11158          * @param {Proxy} 
11159          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11160          * @param {Object} load options 
11161          * @param {Object} jsonData from your request (normally this contains the Exception)
11162          */
11163         loadexception : true
11164     });
11165     
11166     if(this.proxy){
11167         this.proxy = Roo.factory(this.proxy, Roo.data);
11168         this.proxy.xmodule = this.xmodule || false;
11169         this.relayEvents(this.proxy,  ["loadexception"]);
11170     }
11171     this.sortToggle = {};
11172     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11173
11174     Roo.data.Store.superclass.constructor.call(this);
11175
11176     if(this.inlineData){
11177         this.loadData(this.inlineData);
11178         delete this.inlineData;
11179     }
11180 };
11181
11182 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11183      /**
11184     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11185     * without a remote query - used by combo/forms at present.
11186     */
11187     
11188     /**
11189     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11190     */
11191     /**
11192     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11193     */
11194     /**
11195     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11196     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11197     */
11198     /**
11199     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11200     * on any HTTP request
11201     */
11202     /**
11203     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11204     */
11205     /**
11206     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11207     */
11208     multiSort: false,
11209     /**
11210     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11211     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11212     */
11213     remoteSort : false,
11214
11215     /**
11216     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11217      * loaded or when a record is removed. (defaults to false).
11218     */
11219     pruneModifiedRecords : false,
11220
11221     // private
11222     lastOptions : null,
11223
11224     /**
11225      * Add Records to the Store and fires the add event.
11226      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11227      */
11228     add : function(records){
11229         records = [].concat(records);
11230         for(var i = 0, len = records.length; i < len; i++){
11231             records[i].join(this);
11232         }
11233         var index = this.data.length;
11234         this.data.addAll(records);
11235         this.fireEvent("add", this, records, index);
11236     },
11237
11238     /**
11239      * Remove a Record from the Store and fires the remove event.
11240      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11241      */
11242     remove : function(record){
11243         var index = this.data.indexOf(record);
11244         this.data.removeAt(index);
11245  
11246         if(this.pruneModifiedRecords){
11247             this.modified.remove(record);
11248         }
11249         this.fireEvent("remove", this, record, index);
11250     },
11251
11252     /**
11253      * Remove all Records from the Store and fires the clear event.
11254      */
11255     removeAll : function(){
11256         this.data.clear();
11257         if(this.pruneModifiedRecords){
11258             this.modified = [];
11259         }
11260         this.fireEvent("clear", this);
11261     },
11262
11263     /**
11264      * Inserts Records to the Store at the given index and fires the add event.
11265      * @param {Number} index The start index at which to insert the passed Records.
11266      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11267      */
11268     insert : function(index, records){
11269         records = [].concat(records);
11270         for(var i = 0, len = records.length; i < len; i++){
11271             this.data.insert(index, records[i]);
11272             records[i].join(this);
11273         }
11274         this.fireEvent("add", this, records, index);
11275     },
11276
11277     /**
11278      * Get the index within the cache of the passed Record.
11279      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11280      * @return {Number} The index of the passed Record. Returns -1 if not found.
11281      */
11282     indexOf : function(record){
11283         return this.data.indexOf(record);
11284     },
11285
11286     /**
11287      * Get the index within the cache of the Record with the passed id.
11288      * @param {String} id The id of the Record to find.
11289      * @return {Number} The index of the Record. Returns -1 if not found.
11290      */
11291     indexOfId : function(id){
11292         return this.data.indexOfKey(id);
11293     },
11294
11295     /**
11296      * Get the Record with the specified id.
11297      * @param {String} id The id of the Record to find.
11298      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11299      */
11300     getById : function(id){
11301         return this.data.key(id);
11302     },
11303
11304     /**
11305      * Get the Record at the specified index.
11306      * @param {Number} index The index of the Record to find.
11307      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11308      */
11309     getAt : function(index){
11310         return this.data.itemAt(index);
11311     },
11312
11313     /**
11314      * Returns a range of Records between specified indices.
11315      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11316      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11317      * @return {Roo.data.Record[]} An array of Records
11318      */
11319     getRange : function(start, end){
11320         return this.data.getRange(start, end);
11321     },
11322
11323     // private
11324     storeOptions : function(o){
11325         o = Roo.apply({}, o);
11326         delete o.callback;
11327         delete o.scope;
11328         this.lastOptions = o;
11329     },
11330
11331     /**
11332      * Loads the Record cache from the configured Proxy using the configured Reader.
11333      * <p>
11334      * If using remote paging, then the first load call must specify the <em>start</em>
11335      * and <em>limit</em> properties in the options.params property to establish the initial
11336      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11337      * <p>
11338      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11339      * and this call will return before the new data has been loaded. Perform any post-processing
11340      * in a callback function, or in a "load" event handler.</strong>
11341      * <p>
11342      * @param {Object} options An object containing properties which control loading options:<ul>
11343      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11344      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11345      * passed the following arguments:<ul>
11346      * <li>r : Roo.data.Record[]</li>
11347      * <li>options: Options object from the load call</li>
11348      * <li>success: Boolean success indicator</li></ul></li>
11349      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11350      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11351      * </ul>
11352      */
11353     load : function(options){
11354         options = options || {};
11355         if(this.fireEvent("beforeload", this, options) !== false){
11356             this.storeOptions(options);
11357             var p = Roo.apply(options.params || {}, this.baseParams);
11358             // if meta was not loaded from remote source.. try requesting it.
11359             if (!this.reader.metaFromRemote) {
11360                 p._requestMeta = 1;
11361             }
11362             if(this.sortInfo && this.remoteSort){
11363                 var pn = this.paramNames;
11364                 p[pn["sort"]] = this.sortInfo.field;
11365                 p[pn["dir"]] = this.sortInfo.direction;
11366             }
11367             if (this.multiSort) {
11368                 var pn = this.paramNames;
11369                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11370             }
11371             
11372             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11373         }
11374     },
11375
11376     /**
11377      * Reloads the Record cache from the configured Proxy using the configured Reader and
11378      * the options from the last load operation performed.
11379      * @param {Object} options (optional) An object containing properties which may override the options
11380      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11381      * the most recently used options are reused).
11382      */
11383     reload : function(options){
11384         this.load(Roo.applyIf(options||{}, this.lastOptions));
11385     },
11386
11387     // private
11388     // Called as a callback by the Reader during a load operation.
11389     loadRecords : function(o, options, success){
11390         if(!o || success === false){
11391             if(success !== false){
11392                 this.fireEvent("load", this, [], options, o);
11393             }
11394             if(options.callback){
11395                 options.callback.call(options.scope || this, [], options, false);
11396             }
11397             return;
11398         }
11399         // if data returned failure - throw an exception.
11400         if (o.success === false) {
11401             // show a message if no listener is registered.
11402             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11403                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11404             }
11405             // loadmask wil be hooked into this..
11406             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11407             return;
11408         }
11409         var r = o.records, t = o.totalRecords || r.length;
11410         
11411         this.fireEvent("beforeloadadd", this, r, options, o);
11412         
11413         if(!options || options.add !== true){
11414             if(this.pruneModifiedRecords){
11415                 this.modified = [];
11416             }
11417             for(var i = 0, len = r.length; i < len; i++){
11418                 r[i].join(this);
11419             }
11420             if(this.snapshot){
11421                 this.data = this.snapshot;
11422                 delete this.snapshot;
11423             }
11424             this.data.clear();
11425             this.data.addAll(r);
11426             this.totalLength = t;
11427             this.applySort();
11428             this.fireEvent("datachanged", this);
11429         }else{
11430             this.totalLength = Math.max(t, this.data.length+r.length);
11431             this.add(r);
11432         }
11433         
11434         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11435                 
11436             var e = new Roo.data.Record({});
11437
11438             e.set(this.parent.displayField, this.parent.emptyTitle);
11439             e.set(this.parent.valueField, '');
11440
11441             this.insert(0, e);
11442         }
11443             
11444         this.fireEvent("load", this, r, options, o);
11445         if(options.callback){
11446             options.callback.call(options.scope || this, r, options, true);
11447         }
11448     },
11449
11450
11451     /**
11452      * Loads data from a passed data block. A Reader which understands the format of the data
11453      * must have been configured in the constructor.
11454      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11455      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11456      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11457      */
11458     loadData : function(o, append){
11459         var r = this.reader.readRecords(o);
11460         this.loadRecords(r, {add: append}, true);
11461     },
11462
11463     /**
11464      * Gets the number of cached records.
11465      * <p>
11466      * <em>If using paging, this may not be the total size of the dataset. If the data object
11467      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11468      * the data set size</em>
11469      */
11470     getCount : function(){
11471         return this.data.length || 0;
11472     },
11473
11474     /**
11475      * Gets the total number of records in the dataset as returned by the server.
11476      * <p>
11477      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11478      * the dataset size</em>
11479      */
11480     getTotalCount : function(){
11481         return this.totalLength || 0;
11482     },
11483
11484     /**
11485      * Returns the sort state of the Store as an object with two properties:
11486      * <pre><code>
11487  field {String} The name of the field by which the Records are sorted
11488  direction {String} The sort order, "ASC" or "DESC"
11489      * </code></pre>
11490      */
11491     getSortState : function(){
11492         return this.sortInfo;
11493     },
11494
11495     // private
11496     applySort : function(){
11497         if(this.sortInfo && !this.remoteSort){
11498             var s = this.sortInfo, f = s.field;
11499             var st = this.fields.get(f).sortType;
11500             var fn = function(r1, r2){
11501                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11502                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11503             };
11504             this.data.sort(s.direction, fn);
11505             if(this.snapshot && this.snapshot != this.data){
11506                 this.snapshot.sort(s.direction, fn);
11507             }
11508         }
11509     },
11510
11511     /**
11512      * Sets the default sort column and order to be used by the next load operation.
11513      * @param {String} fieldName The name of the field to sort by.
11514      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11515      */
11516     setDefaultSort : function(field, dir){
11517         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11518     },
11519
11520     /**
11521      * Sort the Records.
11522      * If remote sorting is used, the sort is performed on the server, and the cache is
11523      * reloaded. If local sorting is used, the cache is sorted internally.
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     sort : function(fieldName, dir){
11528         var f = this.fields.get(fieldName);
11529         if(!dir){
11530             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11531             
11532             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11533                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11534             }else{
11535                 dir = f.sortDir;
11536             }
11537         }
11538         this.sortToggle[f.name] = dir;
11539         this.sortInfo = {field: f.name, direction: dir};
11540         if(!this.remoteSort){
11541             this.applySort();
11542             this.fireEvent("datachanged", this);
11543         }else{
11544             this.load(this.lastOptions);
11545         }
11546     },
11547
11548     /**
11549      * Calls the specified function for each of the Records in the cache.
11550      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11551      * Returning <em>false</em> aborts and exits the iteration.
11552      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11553      */
11554     each : function(fn, scope){
11555         this.data.each(fn, scope);
11556     },
11557
11558     /**
11559      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11560      * (e.g., during paging).
11561      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11562      */
11563     getModifiedRecords : function(){
11564         return this.modified;
11565     },
11566
11567     // private
11568     createFilterFn : function(property, value, anyMatch){
11569         if(!value.exec){ // not a regex
11570             value = String(value);
11571             if(value.length == 0){
11572                 return false;
11573             }
11574             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11575         }
11576         return function(r){
11577             return value.test(r.data[property]);
11578         };
11579     },
11580
11581     /**
11582      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11583      * @param {String} property A field on your records
11584      * @param {Number} start The record index to start at (defaults to 0)
11585      * @param {Number} end The last record index to include (defaults to length - 1)
11586      * @return {Number} The sum
11587      */
11588     sum : function(property, start, end){
11589         var rs = this.data.items, v = 0;
11590         start = start || 0;
11591         end = (end || end === 0) ? end : rs.length-1;
11592
11593         for(var i = start; i <= end; i++){
11594             v += (rs[i].data[property] || 0);
11595         }
11596         return v;
11597     },
11598
11599     /**
11600      * Filter the records by a specified property.
11601      * @param {String} field A field on your records
11602      * @param {String/RegExp} value Either a string that the field
11603      * should start with or a RegExp to test against the field
11604      * @param {Boolean} anyMatch True to match any part not just the beginning
11605      */
11606     filter : function(property, value, anyMatch){
11607         var fn = this.createFilterFn(property, value, anyMatch);
11608         return fn ? this.filterBy(fn) : this.clearFilter();
11609     },
11610
11611     /**
11612      * Filter by a function. The specified function will be called with each
11613      * record in this data source. If the function returns true the record is included,
11614      * otherwise it is filtered.
11615      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11616      * @param {Object} scope (optional) The scope of the function (defaults to this)
11617      */
11618     filterBy : function(fn, scope){
11619         this.snapshot = this.snapshot || this.data;
11620         this.data = this.queryBy(fn, scope||this);
11621         this.fireEvent("datachanged", this);
11622     },
11623
11624     /**
11625      * Query the records by a specified property.
11626      * @param {String} field A field on your records
11627      * @param {String/RegExp} value Either a string that the field
11628      * should start with or a RegExp to test against the field
11629      * @param {Boolean} anyMatch True to match any part not just the beginning
11630      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11631      */
11632     query : function(property, value, anyMatch){
11633         var fn = this.createFilterFn(property, value, anyMatch);
11634         return fn ? this.queryBy(fn) : this.data.clone();
11635     },
11636
11637     /**
11638      * Query by a function. The specified function will be called with each
11639      * record in this data source. If the function returns true the record is included
11640      * in the results.
11641      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11642      * @param {Object} scope (optional) The scope of the function (defaults to this)
11643       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11644      **/
11645     queryBy : function(fn, scope){
11646         var data = this.snapshot || this.data;
11647         return data.filterBy(fn, scope||this);
11648     },
11649
11650     /**
11651      * Collects unique values for a particular dataIndex from this store.
11652      * @param {String} dataIndex The property to collect
11653      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11654      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11655      * @return {Array} An array of the unique values
11656      **/
11657     collect : function(dataIndex, allowNull, bypassFilter){
11658         var d = (bypassFilter === true && this.snapshot) ?
11659                 this.snapshot.items : this.data.items;
11660         var v, sv, r = [], l = {};
11661         for(var i = 0, len = d.length; i < len; i++){
11662             v = d[i].data[dataIndex];
11663             sv = String(v);
11664             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11665                 l[sv] = true;
11666                 r[r.length] = v;
11667             }
11668         }
11669         return r;
11670     },
11671
11672     /**
11673      * Revert to a view of the Record cache with no filtering applied.
11674      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11675      */
11676     clearFilter : function(suppressEvent){
11677         if(this.snapshot && this.snapshot != this.data){
11678             this.data = this.snapshot;
11679             delete this.snapshot;
11680             if(suppressEvent !== true){
11681                 this.fireEvent("datachanged", this);
11682             }
11683         }
11684     },
11685
11686     // private
11687     afterEdit : function(record){
11688         if(this.modified.indexOf(record) == -1){
11689             this.modified.push(record);
11690         }
11691         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11692     },
11693     
11694     // private
11695     afterReject : function(record){
11696         this.modified.remove(record);
11697         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11698     },
11699
11700     // private
11701     afterCommit : function(record){
11702         this.modified.remove(record);
11703         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11704     },
11705
11706     /**
11707      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11708      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11709      */
11710     commitChanges : function(){
11711         var m = this.modified.slice(0);
11712         this.modified = [];
11713         for(var i = 0, len = m.length; i < len; i++){
11714             m[i].commit();
11715         }
11716     },
11717
11718     /**
11719      * Cancel outstanding changes on all changed records.
11720      */
11721     rejectChanges : 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].reject();
11726         }
11727     },
11728
11729     onMetaChange : function(meta, rtype, o){
11730         this.recordType = rtype;
11731         this.fields = rtype.prototype.fields;
11732         delete this.snapshot;
11733         this.sortInfo = meta.sortInfo || this.sortInfo;
11734         this.modified = [];
11735         this.fireEvent('metachange', this, this.reader.meta);
11736     },
11737     
11738     moveIndex : function(data, type)
11739     {
11740         var index = this.indexOf(data);
11741         
11742         var newIndex = index + type;
11743         
11744         this.remove(data);
11745         
11746         this.insert(newIndex, data);
11747         
11748     }
11749 });/*
11750  * Based on:
11751  * Ext JS Library 1.1.1
11752  * Copyright(c) 2006-2007, Ext JS, LLC.
11753  *
11754  * Originally Released Under LGPL - original licence link has changed is not relivant.
11755  *
11756  * Fork - LGPL
11757  * <script type="text/javascript">
11758  */
11759
11760 /**
11761  * @class Roo.data.SimpleStore
11762  * @extends Roo.data.Store
11763  * Small helper class to make creating Stores from Array data easier.
11764  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11765  * @cfg {Array} fields An array of field definition objects, or field name strings.
11766  * @cfg {Array} data The multi-dimensional array of data
11767  * @constructor
11768  * @param {Object} config
11769  */
11770 Roo.data.SimpleStore = function(config){
11771     Roo.data.SimpleStore.superclass.constructor.call(this, {
11772         isLocal : true,
11773         reader: new Roo.data.ArrayReader({
11774                 id: config.id
11775             },
11776             Roo.data.Record.create(config.fields)
11777         ),
11778         proxy : new Roo.data.MemoryProxy(config.data)
11779     });
11780     this.load();
11781 };
11782 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11783  * Based on:
11784  * Ext JS Library 1.1.1
11785  * Copyright(c) 2006-2007, Ext JS, LLC.
11786  *
11787  * Originally Released Under LGPL - original licence link has changed is not relivant.
11788  *
11789  * Fork - LGPL
11790  * <script type="text/javascript">
11791  */
11792
11793 /**
11794 /**
11795  * @extends Roo.data.Store
11796  * @class Roo.data.JsonStore
11797  * Small helper class to make creating Stores for JSON data easier. <br/>
11798 <pre><code>
11799 var store = new Roo.data.JsonStore({
11800     url: 'get-images.php',
11801     root: 'images',
11802     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11803 });
11804 </code></pre>
11805  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11806  * JsonReader and HttpProxy (unless inline data is provided).</b>
11807  * @cfg {Array} fields An array of field definition objects, or field name strings.
11808  * @constructor
11809  * @param {Object} config
11810  */
11811 Roo.data.JsonStore = function(c){
11812     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11813         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11814         reader: new Roo.data.JsonReader(c, c.fields)
11815     }));
11816 };
11817 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11818  * Based on:
11819  * Ext JS Library 1.1.1
11820  * Copyright(c) 2006-2007, Ext JS, LLC.
11821  *
11822  * Originally Released Under LGPL - original licence link has changed is not relivant.
11823  *
11824  * Fork - LGPL
11825  * <script type="text/javascript">
11826  */
11827
11828  
11829 Roo.data.Field = function(config){
11830     if(typeof config == "string"){
11831         config = {name: config};
11832     }
11833     Roo.apply(this, config);
11834     
11835     if(!this.type){
11836         this.type = "auto";
11837     }
11838     
11839     var st = Roo.data.SortTypes;
11840     // named sortTypes are supported, here we look them up
11841     if(typeof this.sortType == "string"){
11842         this.sortType = st[this.sortType];
11843     }
11844     
11845     // set default sortType for strings and dates
11846     if(!this.sortType){
11847         switch(this.type){
11848             case "string":
11849                 this.sortType = st.asUCString;
11850                 break;
11851             case "date":
11852                 this.sortType = st.asDate;
11853                 break;
11854             default:
11855                 this.sortType = st.none;
11856         }
11857     }
11858
11859     // define once
11860     var stripRe = /[\$,%]/g;
11861
11862     // prebuilt conversion function for this field, instead of
11863     // switching every time we're reading a value
11864     if(!this.convert){
11865         var cv, dateFormat = this.dateFormat;
11866         switch(this.type){
11867             case "":
11868             case "auto":
11869             case undefined:
11870                 cv = function(v){ return v; };
11871                 break;
11872             case "string":
11873                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11874                 break;
11875             case "int":
11876                 cv = function(v){
11877                     return v !== undefined && v !== null && v !== '' ?
11878                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11879                     };
11880                 break;
11881             case "float":
11882                 cv = function(v){
11883                     return v !== undefined && v !== null && v !== '' ?
11884                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11885                     };
11886                 break;
11887             case "bool":
11888             case "boolean":
11889                 cv = function(v){ return v === true || v === "true" || v == 1; };
11890                 break;
11891             case "date":
11892                 cv = function(v){
11893                     if(!v){
11894                         return '';
11895                     }
11896                     if(v instanceof Date){
11897                         return v;
11898                     }
11899                     if(dateFormat){
11900                         if(dateFormat == "timestamp"){
11901                             return new Date(v*1000);
11902                         }
11903                         return Date.parseDate(v, dateFormat);
11904                     }
11905                     var parsed = Date.parse(v);
11906                     return parsed ? new Date(parsed) : null;
11907                 };
11908              break;
11909             
11910         }
11911         this.convert = cv;
11912     }
11913 };
11914
11915 Roo.data.Field.prototype = {
11916     dateFormat: null,
11917     defaultValue: "",
11918     mapping: null,
11919     sortType : null,
11920     sortDir : "ASC"
11921 };/*
11922  * Based on:
11923  * Ext JS Library 1.1.1
11924  * Copyright(c) 2006-2007, Ext JS, LLC.
11925  *
11926  * Originally Released Under LGPL - original licence link has changed is not relivant.
11927  *
11928  * Fork - LGPL
11929  * <script type="text/javascript">
11930  */
11931  
11932 // Base class for reading structured data from a data source.  This class is intended to be
11933 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11934
11935 /**
11936  * @class Roo.data.DataReader
11937  * Base class for reading structured data from a data source.  This class is intended to be
11938  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11939  */
11940
11941 Roo.data.DataReader = function(meta, recordType){
11942     
11943     this.meta = meta;
11944     
11945     this.recordType = recordType instanceof Array ? 
11946         Roo.data.Record.create(recordType) : recordType;
11947 };
11948
11949 Roo.data.DataReader.prototype = {
11950      /**
11951      * Create an empty record
11952      * @param {Object} data (optional) - overlay some values
11953      * @return {Roo.data.Record} record created.
11954      */
11955     newRow :  function(d) {
11956         var da =  {};
11957         this.recordType.prototype.fields.each(function(c) {
11958             switch( c.type) {
11959                 case 'int' : da[c.name] = 0; break;
11960                 case 'date' : da[c.name] = new Date(); break;
11961                 case 'float' : da[c.name] = 0.0; break;
11962                 case 'boolean' : da[c.name] = false; break;
11963                 default : da[c.name] = ""; break;
11964             }
11965             
11966         });
11967         return new this.recordType(Roo.apply(da, d));
11968     }
11969     
11970 };/*
11971  * Based on:
11972  * Ext JS Library 1.1.1
11973  * Copyright(c) 2006-2007, Ext JS, LLC.
11974  *
11975  * Originally Released Under LGPL - original licence link has changed is not relivant.
11976  *
11977  * Fork - LGPL
11978  * <script type="text/javascript">
11979  */
11980
11981 /**
11982  * @class Roo.data.DataProxy
11983  * @extends Roo.data.Observable
11984  * This class is an abstract base class for implementations which provide retrieval of
11985  * unformatted data objects.<br>
11986  * <p>
11987  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11988  * (of the appropriate type which knows how to parse the data object) to provide a block of
11989  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11990  * <p>
11991  * Custom implementations must implement the load method as described in
11992  * {@link Roo.data.HttpProxy#load}.
11993  */
11994 Roo.data.DataProxy = function(){
11995     this.addEvents({
11996         /**
11997          * @event beforeload
11998          * Fires before a network request is made to retrieve a data object.
11999          * @param {Object} This DataProxy object.
12000          * @param {Object} params The params parameter to the load function.
12001          */
12002         beforeload : true,
12003         /**
12004          * @event load
12005          * Fires before the load method's callback is called.
12006          * @param {Object} This DataProxy object.
12007          * @param {Object} o The data object.
12008          * @param {Object} arg The callback argument object passed to the load function.
12009          */
12010         load : true,
12011         /**
12012          * @event loadexception
12013          * Fires if an Exception occurs during data retrieval.
12014          * @param {Object} This DataProxy object.
12015          * @param {Object} o The data object.
12016          * @param {Object} arg The callback argument object passed to the load function.
12017          * @param {Object} e The Exception.
12018          */
12019         loadexception : true
12020     });
12021     Roo.data.DataProxy.superclass.constructor.call(this);
12022 };
12023
12024 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12025
12026     /**
12027      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12028      */
12029 /*
12030  * Based on:
12031  * Ext JS Library 1.1.1
12032  * Copyright(c) 2006-2007, Ext JS, LLC.
12033  *
12034  * Originally Released Under LGPL - original licence link has changed is not relivant.
12035  *
12036  * Fork - LGPL
12037  * <script type="text/javascript">
12038  */
12039 /**
12040  * @class Roo.data.MemoryProxy
12041  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12042  * to the Reader when its load method is called.
12043  * @constructor
12044  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12045  */
12046 Roo.data.MemoryProxy = function(data){
12047     if (data.data) {
12048         data = data.data;
12049     }
12050     Roo.data.MemoryProxy.superclass.constructor.call(this);
12051     this.data = data;
12052 };
12053
12054 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12055     
12056     /**
12057      * Load data from the requested source (in this case an in-memory
12058      * data object passed to the constructor), read the data object into
12059      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12060      * process that block using the passed callback.
12061      * @param {Object} params This parameter is not used by the MemoryProxy class.
12062      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12063      * object into a block of Roo.data.Records.
12064      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12065      * The function must be passed <ul>
12066      * <li>The Record block object</li>
12067      * <li>The "arg" argument from the load function</li>
12068      * <li>A boolean success indicator</li>
12069      * </ul>
12070      * @param {Object} scope The scope in which to call the callback
12071      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12072      */
12073     load : function(params, reader, callback, scope, arg){
12074         params = params || {};
12075         var result;
12076         try {
12077             result = reader.readRecords(this.data);
12078         }catch(e){
12079             this.fireEvent("loadexception", this, arg, null, e);
12080             callback.call(scope, null, arg, false);
12081             return;
12082         }
12083         callback.call(scope, result, arg, true);
12084     },
12085     
12086     // private
12087     update : function(params, records){
12088         
12089     }
12090 });/*
12091  * Based on:
12092  * Ext JS Library 1.1.1
12093  * Copyright(c) 2006-2007, Ext JS, LLC.
12094  *
12095  * Originally Released Under LGPL - original licence link has changed is not relivant.
12096  *
12097  * Fork - LGPL
12098  * <script type="text/javascript">
12099  */
12100 /**
12101  * @class Roo.data.HttpProxy
12102  * @extends Roo.data.DataProxy
12103  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12104  * configured to reference a certain URL.<br><br>
12105  * <p>
12106  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12107  * from which the running page was served.<br><br>
12108  * <p>
12109  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12110  * <p>
12111  * Be aware that to enable the browser to parse an XML document, the server must set
12112  * the Content-Type header in the HTTP response to "text/xml".
12113  * @constructor
12114  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12115  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12116  * will be used to make the request.
12117  */
12118 Roo.data.HttpProxy = function(conn){
12119     Roo.data.HttpProxy.superclass.constructor.call(this);
12120     // is conn a conn config or a real conn?
12121     this.conn = conn;
12122     this.useAjax = !conn || !conn.events;
12123   
12124 };
12125
12126 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12127     // thse are take from connection...
12128     
12129     /**
12130      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12131      */
12132     /**
12133      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12134      * extra parameters to each request made by this object. (defaults to undefined)
12135      */
12136     /**
12137      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12138      *  to each request made by this object. (defaults to undefined)
12139      */
12140     /**
12141      * @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)
12142      */
12143     /**
12144      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12145      */
12146      /**
12147      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12148      * @type Boolean
12149      */
12150   
12151
12152     /**
12153      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12154      * @type Boolean
12155      */
12156     /**
12157      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12158      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12159      * a finer-grained basis than the DataProxy events.
12160      */
12161     getConnection : function(){
12162         return this.useAjax ? Roo.Ajax : this.conn;
12163     },
12164
12165     /**
12166      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12167      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12168      * process that block using the passed callback.
12169      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12170      * for the request to the remote server.
12171      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12172      * object into a block of Roo.data.Records.
12173      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12174      * The function must be passed <ul>
12175      * <li>The Record block object</li>
12176      * <li>The "arg" argument from the load function</li>
12177      * <li>A boolean success indicator</li>
12178      * </ul>
12179      * @param {Object} scope The scope in which to call the callback
12180      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12181      */
12182     load : function(params, reader, callback, scope, arg){
12183         if(this.fireEvent("beforeload", this, params) !== false){
12184             var  o = {
12185                 params : params || {},
12186                 request: {
12187                     callback : callback,
12188                     scope : scope,
12189                     arg : arg
12190                 },
12191                 reader: reader,
12192                 callback : this.loadResponse,
12193                 scope: this
12194             };
12195             if(this.useAjax){
12196                 Roo.applyIf(o, this.conn);
12197                 if(this.activeRequest){
12198                     Roo.Ajax.abort(this.activeRequest);
12199                 }
12200                 this.activeRequest = Roo.Ajax.request(o);
12201             }else{
12202                 this.conn.request(o);
12203             }
12204         }else{
12205             callback.call(scope||this, null, arg, false);
12206         }
12207     },
12208
12209     // private
12210     loadResponse : function(o, success, response){
12211         delete this.activeRequest;
12212         if(!success){
12213             this.fireEvent("loadexception", this, o, response);
12214             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12215             return;
12216         }
12217         var result;
12218         try {
12219             result = o.reader.read(response);
12220         }catch(e){
12221             this.fireEvent("loadexception", this, o, response, e);
12222             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12223             return;
12224         }
12225         
12226         this.fireEvent("load", this, o, o.request.arg);
12227         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12228     },
12229
12230     // private
12231     update : function(dataSet){
12232
12233     },
12234
12235     // private
12236     updateResponse : function(dataSet){
12237
12238     }
12239 });/*
12240  * Based on:
12241  * Ext JS Library 1.1.1
12242  * Copyright(c) 2006-2007, Ext JS, LLC.
12243  *
12244  * Originally Released Under LGPL - original licence link has changed is not relivant.
12245  *
12246  * Fork - LGPL
12247  * <script type="text/javascript">
12248  */
12249
12250 /**
12251  * @class Roo.data.ScriptTagProxy
12252  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12253  * other than the originating domain of the running page.<br><br>
12254  * <p>
12255  * <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
12256  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12257  * <p>
12258  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12259  * source code that is used as the source inside a &lt;script> tag.<br><br>
12260  * <p>
12261  * In order for the browser to process the returned data, the server must wrap the data object
12262  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12263  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12264  * depending on whether the callback name was passed:
12265  * <p>
12266  * <pre><code>
12267 boolean scriptTag = false;
12268 String cb = request.getParameter("callback");
12269 if (cb != null) {
12270     scriptTag = true;
12271     response.setContentType("text/javascript");
12272 } else {
12273     response.setContentType("application/x-json");
12274 }
12275 Writer out = response.getWriter();
12276 if (scriptTag) {
12277     out.write(cb + "(");
12278 }
12279 out.print(dataBlock.toJsonString());
12280 if (scriptTag) {
12281     out.write(");");
12282 }
12283 </pre></code>
12284  *
12285  * @constructor
12286  * @param {Object} config A configuration object.
12287  */
12288 Roo.data.ScriptTagProxy = function(config){
12289     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12290     Roo.apply(this, config);
12291     this.head = document.getElementsByTagName("head")[0];
12292 };
12293
12294 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12295
12296 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12297     /**
12298      * @cfg {String} url The URL from which to request the data object.
12299      */
12300     /**
12301      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12302      */
12303     timeout : 30000,
12304     /**
12305      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12306      * the server the name of the callback function set up by the load call to process the returned data object.
12307      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12308      * javascript output which calls this named function passing the data object as its only parameter.
12309      */
12310     callbackParam : "callback",
12311     /**
12312      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12313      * name to the request.
12314      */
12315     nocache : true,
12316
12317     /**
12318      * Load data from the configured URL, read the data object into
12319      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12320      * process that block using the passed callback.
12321      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12322      * for the request to the remote server.
12323      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12324      * object into a block of Roo.data.Records.
12325      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12326      * The function must be passed <ul>
12327      * <li>The Record block object</li>
12328      * <li>The "arg" argument from the load function</li>
12329      * <li>A boolean success indicator</li>
12330      * </ul>
12331      * @param {Object} scope The scope in which to call the callback
12332      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12333      */
12334     load : function(params, reader, callback, scope, arg){
12335         if(this.fireEvent("beforeload", this, params) !== false){
12336
12337             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12338
12339             var url = this.url;
12340             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12341             if(this.nocache){
12342                 url += "&_dc=" + (new Date().getTime());
12343             }
12344             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12345             var trans = {
12346                 id : transId,
12347                 cb : "stcCallback"+transId,
12348                 scriptId : "stcScript"+transId,
12349                 params : params,
12350                 arg : arg,
12351                 url : url,
12352                 callback : callback,
12353                 scope : scope,
12354                 reader : reader
12355             };
12356             var conn = this;
12357
12358             window[trans.cb] = function(o){
12359                 conn.handleResponse(o, trans);
12360             };
12361
12362             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12363
12364             if(this.autoAbort !== false){
12365                 this.abort();
12366             }
12367
12368             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12369
12370             var script = document.createElement("script");
12371             script.setAttribute("src", url);
12372             script.setAttribute("type", "text/javascript");
12373             script.setAttribute("id", trans.scriptId);
12374             this.head.appendChild(script);
12375
12376             this.trans = trans;
12377         }else{
12378             callback.call(scope||this, null, arg, false);
12379         }
12380     },
12381
12382     // private
12383     isLoading : function(){
12384         return this.trans ? true : false;
12385     },
12386
12387     /**
12388      * Abort the current server request.
12389      */
12390     abort : function(){
12391         if(this.isLoading()){
12392             this.destroyTrans(this.trans);
12393         }
12394     },
12395
12396     // private
12397     destroyTrans : function(trans, isLoaded){
12398         this.head.removeChild(document.getElementById(trans.scriptId));
12399         clearTimeout(trans.timeoutId);
12400         if(isLoaded){
12401             window[trans.cb] = undefined;
12402             try{
12403                 delete window[trans.cb];
12404             }catch(e){}
12405         }else{
12406             // if hasn't been loaded, wait for load to remove it to prevent script error
12407             window[trans.cb] = function(){
12408                 window[trans.cb] = undefined;
12409                 try{
12410                     delete window[trans.cb];
12411                 }catch(e){}
12412             };
12413         }
12414     },
12415
12416     // private
12417     handleResponse : function(o, trans){
12418         this.trans = false;
12419         this.destroyTrans(trans, true);
12420         var result;
12421         try {
12422             result = trans.reader.readRecords(o);
12423         }catch(e){
12424             this.fireEvent("loadexception", this, o, trans.arg, e);
12425             trans.callback.call(trans.scope||window, null, trans.arg, false);
12426             return;
12427         }
12428         this.fireEvent("load", this, o, trans.arg);
12429         trans.callback.call(trans.scope||window, result, trans.arg, true);
12430     },
12431
12432     // private
12433     handleFailure : function(trans){
12434         this.trans = false;
12435         this.destroyTrans(trans, false);
12436         this.fireEvent("loadexception", this, null, trans.arg);
12437         trans.callback.call(trans.scope||window, null, trans.arg, false);
12438     }
12439 });/*
12440  * Based on:
12441  * Ext JS Library 1.1.1
12442  * Copyright(c) 2006-2007, Ext JS, LLC.
12443  *
12444  * Originally Released Under LGPL - original licence link has changed is not relivant.
12445  *
12446  * Fork - LGPL
12447  * <script type="text/javascript">
12448  */
12449
12450 /**
12451  * @class Roo.data.JsonReader
12452  * @extends Roo.data.DataReader
12453  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12454  * based on mappings in a provided Roo.data.Record constructor.
12455  * 
12456  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12457  * in the reply previously. 
12458  * 
12459  * <p>
12460  * Example code:
12461  * <pre><code>
12462 var RecordDef = Roo.data.Record.create([
12463     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12464     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12465 ]);
12466 var myReader = new Roo.data.JsonReader({
12467     totalProperty: "results",    // The property which contains the total dataset size (optional)
12468     root: "rows",                // The property which contains an Array of row objects
12469     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12470 }, RecordDef);
12471 </code></pre>
12472  * <p>
12473  * This would consume a JSON file like this:
12474  * <pre><code>
12475 { 'results': 2, 'rows': [
12476     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12477     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12478 }
12479 </code></pre>
12480  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12481  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12482  * paged from the remote server.
12483  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12484  * @cfg {String} root name of the property which contains the Array of row objects.
12485  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12486  * @cfg {Array} fields Array of field definition objects
12487  * @constructor
12488  * Create a new JsonReader
12489  * @param {Object} meta Metadata configuration options
12490  * @param {Object} recordType Either an Array of field definition objects,
12491  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12492  */
12493 Roo.data.JsonReader = function(meta, recordType){
12494     
12495     meta = meta || {};
12496     // set some defaults:
12497     Roo.applyIf(meta, {
12498         totalProperty: 'total',
12499         successProperty : 'success',
12500         root : 'data',
12501         id : 'id'
12502     });
12503     
12504     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12505 };
12506 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12507     
12508     /**
12509      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12510      * Used by Store query builder to append _requestMeta to params.
12511      * 
12512      */
12513     metaFromRemote : false,
12514     /**
12515      * This method is only used by a DataProxy which has retrieved data from a remote server.
12516      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12517      * @return {Object} data A data block which is used by an Roo.data.Store object as
12518      * a cache of Roo.data.Records.
12519      */
12520     read : function(response){
12521         var json = response.responseText;
12522        
12523         var o = /* eval:var:o */ eval("("+json+")");
12524         if(!o) {
12525             throw {message: "JsonReader.read: Json object not found"};
12526         }
12527         
12528         if(o.metaData){
12529             
12530             delete this.ef;
12531             this.metaFromRemote = true;
12532             this.meta = o.metaData;
12533             this.recordType = Roo.data.Record.create(o.metaData.fields);
12534             this.onMetaChange(this.meta, this.recordType, o);
12535         }
12536         return this.readRecords(o);
12537     },
12538
12539     // private function a store will implement
12540     onMetaChange : function(meta, recordType, o){
12541
12542     },
12543
12544     /**
12545          * @ignore
12546          */
12547     simpleAccess: function(obj, subsc) {
12548         return obj[subsc];
12549     },
12550
12551         /**
12552          * @ignore
12553          */
12554     getJsonAccessor: function(){
12555         var re = /[\[\.]/;
12556         return function(expr) {
12557             try {
12558                 return(re.test(expr))
12559                     ? new Function("obj", "return obj." + expr)
12560                     : function(obj){
12561                         return obj[expr];
12562                     };
12563             } catch(e){}
12564             return Roo.emptyFn;
12565         };
12566     }(),
12567
12568     /**
12569      * Create a data block containing Roo.data.Records from an XML document.
12570      * @param {Object} o An object which contains an Array of row objects in the property specified
12571      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12572      * which contains the total size of the dataset.
12573      * @return {Object} data A data block which is used by an Roo.data.Store object as
12574      * a cache of Roo.data.Records.
12575      */
12576     readRecords : function(o){
12577         /**
12578          * After any data loads, the raw JSON data is available for further custom processing.
12579          * @type Object
12580          */
12581         this.o = o;
12582         var s = this.meta, Record = this.recordType,
12583             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12584
12585 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12586         if (!this.ef) {
12587             if(s.totalProperty) {
12588                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12589                 }
12590                 if(s.successProperty) {
12591                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12592                 }
12593                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12594                 if (s.id) {
12595                         var g = this.getJsonAccessor(s.id);
12596                         this.getId = function(rec) {
12597                                 var r = g(rec);  
12598                                 return (r === undefined || r === "") ? null : r;
12599                         };
12600                 } else {
12601                         this.getId = function(){return null;};
12602                 }
12603             this.ef = [];
12604             for(var jj = 0; jj < fl; jj++){
12605                 f = fi[jj];
12606                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12607                 this.ef[jj] = this.getJsonAccessor(map);
12608             }
12609         }
12610
12611         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12612         if(s.totalProperty){
12613             var vt = parseInt(this.getTotal(o), 10);
12614             if(!isNaN(vt)){
12615                 totalRecords = vt;
12616             }
12617         }
12618         if(s.successProperty){
12619             var vs = this.getSuccess(o);
12620             if(vs === false || vs === 'false'){
12621                 success = false;
12622             }
12623         }
12624         var records = [];
12625         for(var i = 0; i < c; i++){
12626                 var n = root[i];
12627             var values = {};
12628             var id = this.getId(n);
12629             for(var j = 0; j < fl; j++){
12630                 f = fi[j];
12631             var v = this.ef[j](n);
12632             if (!f.convert) {
12633                 Roo.log('missing convert for ' + f.name);
12634                 Roo.log(f);
12635                 continue;
12636             }
12637             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12638             }
12639             var record = new Record(values, id);
12640             record.json = n;
12641             records[i] = record;
12642         }
12643         return {
12644             raw : o,
12645             success : success,
12646             records : records,
12647             totalRecords : totalRecords
12648         };
12649     }
12650 });/*
12651  * Based on:
12652  * Ext JS Library 1.1.1
12653  * Copyright(c) 2006-2007, Ext JS, LLC.
12654  *
12655  * Originally Released Under LGPL - original licence link has changed is not relivant.
12656  *
12657  * Fork - LGPL
12658  * <script type="text/javascript">
12659  */
12660
12661 /**
12662  * @class Roo.data.ArrayReader
12663  * @extends Roo.data.DataReader
12664  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12665  * Each element of that Array represents a row of data fields. The
12666  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12667  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12668  * <p>
12669  * Example code:.
12670  * <pre><code>
12671 var RecordDef = Roo.data.Record.create([
12672     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12673     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12674 ]);
12675 var myReader = new Roo.data.ArrayReader({
12676     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12677 }, RecordDef);
12678 </code></pre>
12679  * <p>
12680  * This would consume an Array like this:
12681  * <pre><code>
12682 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12683   </code></pre>
12684  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12685  * @constructor
12686  * Create a new JsonReader
12687  * @param {Object} meta Metadata configuration options.
12688  * @param {Object} recordType Either an Array of field definition objects
12689  * as specified to {@link Roo.data.Record#create},
12690  * or an {@link Roo.data.Record} object
12691  * created using {@link Roo.data.Record#create}.
12692  */
12693 Roo.data.ArrayReader = function(meta, recordType){
12694     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12695 };
12696
12697 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12698     /**
12699      * Create a data block containing Roo.data.Records from an XML document.
12700      * @param {Object} o An Array of row objects which represents the dataset.
12701      * @return {Object} data A data block which is used by an Roo.data.Store object as
12702      * a cache of Roo.data.Records.
12703      */
12704     readRecords : function(o){
12705         var sid = this.meta ? this.meta.id : null;
12706         var recordType = this.recordType, fields = recordType.prototype.fields;
12707         var records = [];
12708         var root = o;
12709             for(var i = 0; i < root.length; i++){
12710                     var n = root[i];
12711                 var values = {};
12712                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12713                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12714                 var f = fields.items[j];
12715                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12716                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12717                 v = f.convert(v);
12718                 values[f.name] = v;
12719             }
12720                 var record = new recordType(values, id);
12721                 record.json = n;
12722                 records[records.length] = record;
12723             }
12724             return {
12725                 records : records,
12726                 totalRecords : records.length
12727             };
12728     }
12729 });/*
12730  * - LGPL
12731  * * 
12732  */
12733
12734 /**
12735  * @class Roo.bootstrap.ComboBox
12736  * @extends Roo.bootstrap.TriggerField
12737  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12738  * @cfg {Boolean} append (true|false) default false
12739  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12740  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12741  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12742  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12743  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12744  * @cfg {Boolean} animate default true
12745  * @cfg {Boolean} emptyResultText only for touch device
12746  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12747  * @cfg {String} emptyTitle default ''
12748  * @constructor
12749  * Create a new ComboBox.
12750  * @param {Object} config Configuration options
12751  */
12752 Roo.bootstrap.ComboBox = function(config){
12753     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12754     this.addEvents({
12755         /**
12756          * @event expand
12757          * Fires when the dropdown list is expanded
12758         * @param {Roo.bootstrap.ComboBox} combo This combo box
12759         */
12760         'expand' : true,
12761         /**
12762          * @event collapse
12763          * Fires when the dropdown list is collapsed
12764         * @param {Roo.bootstrap.ComboBox} combo This combo box
12765         */
12766         'collapse' : true,
12767         /**
12768          * @event beforeselect
12769          * Fires before a list item is selected. Return false to cancel the selection.
12770         * @param {Roo.bootstrap.ComboBox} combo This combo box
12771         * @param {Roo.data.Record} record The data record returned from the underlying store
12772         * @param {Number} index The index of the selected item in the dropdown list
12773         */
12774         'beforeselect' : true,
12775         /**
12776          * @event select
12777          * Fires when a list item is selected
12778         * @param {Roo.bootstrap.ComboBox} combo This combo box
12779         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12780         * @param {Number} index The index of the selected item in the dropdown list
12781         */
12782         'select' : true,
12783         /**
12784          * @event beforequery
12785          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12786          * The event object passed has these properties:
12787         * @param {Roo.bootstrap.ComboBox} combo This combo box
12788         * @param {String} query The query
12789         * @param {Boolean} forceAll true to force "all" query
12790         * @param {Boolean} cancel true to cancel the query
12791         * @param {Object} e The query event object
12792         */
12793         'beforequery': true,
12794          /**
12795          * @event add
12796          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12797         * @param {Roo.bootstrap.ComboBox} combo This combo box
12798         */
12799         'add' : true,
12800         /**
12801          * @event edit
12802          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12803         * @param {Roo.bootstrap.ComboBox} combo This combo box
12804         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12805         */
12806         'edit' : true,
12807         /**
12808          * @event remove
12809          * Fires when the remove value from the combobox array
12810         * @param {Roo.bootstrap.ComboBox} combo This combo box
12811         */
12812         'remove' : true,
12813         /**
12814          * @event afterremove
12815          * Fires when the remove value from the combobox array
12816         * @param {Roo.bootstrap.ComboBox} combo This combo box
12817         */
12818         'afterremove' : true,
12819         /**
12820          * @event specialfilter
12821          * Fires when specialfilter
12822             * @param {Roo.bootstrap.ComboBox} combo This combo box
12823             */
12824         'specialfilter' : true,
12825         /**
12826          * @event tick
12827          * Fires when tick the element
12828             * @param {Roo.bootstrap.ComboBox} combo This combo box
12829             */
12830         'tick' : true,
12831         /**
12832          * @event touchviewdisplay
12833          * Fires when touch view require special display (default is using displayField)
12834             * @param {Roo.bootstrap.ComboBox} combo This combo box
12835             * @param {Object} cfg set html .
12836             */
12837         'touchviewdisplay' : true
12838         
12839     });
12840     
12841     this.item = [];
12842     this.tickItems = [];
12843     
12844     this.selectedIndex = -1;
12845     if(this.mode == 'local'){
12846         if(config.queryDelay === undefined){
12847             this.queryDelay = 10;
12848         }
12849         if(config.minChars === undefined){
12850             this.minChars = 0;
12851         }
12852     }
12853 };
12854
12855 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12856      
12857     /**
12858      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12859      * rendering into an Roo.Editor, defaults to false)
12860      */
12861     /**
12862      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12863      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12864      */
12865     /**
12866      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12867      */
12868     /**
12869      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12870      * the dropdown list (defaults to undefined, with no header element)
12871      */
12872
12873      /**
12874      * @cfg {String/Roo.Template} tpl The template to use to render the output
12875      */
12876      
12877      /**
12878      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12879      */
12880     listWidth: undefined,
12881     /**
12882      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12883      * mode = 'remote' or 'text' if mode = 'local')
12884      */
12885     displayField: undefined,
12886     
12887     /**
12888      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12889      * mode = 'remote' or 'value' if mode = 'local'). 
12890      * Note: use of a valueField requires the user make a selection
12891      * in order for a value to be mapped.
12892      */
12893     valueField: undefined,
12894     /**
12895      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12896      */
12897     modalTitle : '',
12898     
12899     /**
12900      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12901      * field's data value (defaults to the underlying DOM element's name)
12902      */
12903     hiddenName: undefined,
12904     /**
12905      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12906      */
12907     listClass: '',
12908     /**
12909      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12910      */
12911     selectedClass: 'active',
12912     
12913     /**
12914      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12915      */
12916     shadow:'sides',
12917     /**
12918      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12919      * anchor positions (defaults to 'tl-bl')
12920      */
12921     listAlign: 'tl-bl?',
12922     /**
12923      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12924      */
12925     maxHeight: 300,
12926     /**
12927      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12928      * query specified by the allQuery config option (defaults to 'query')
12929      */
12930     triggerAction: 'query',
12931     /**
12932      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12933      * (defaults to 4, does not apply if editable = false)
12934      */
12935     minChars : 4,
12936     /**
12937      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12938      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12939      */
12940     typeAhead: false,
12941     /**
12942      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12943      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12944      */
12945     queryDelay: 500,
12946     /**
12947      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12948      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12949      */
12950     pageSize: 0,
12951     /**
12952      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12953      * when editable = true (defaults to false)
12954      */
12955     selectOnFocus:false,
12956     /**
12957      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12958      */
12959     queryParam: 'query',
12960     /**
12961      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12962      * when mode = 'remote' (defaults to 'Loading...')
12963      */
12964     loadingText: 'Loading...',
12965     /**
12966      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12967      */
12968     resizable: false,
12969     /**
12970      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12971      */
12972     handleHeight : 8,
12973     /**
12974      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12975      * traditional select (defaults to true)
12976      */
12977     editable: true,
12978     /**
12979      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12980      */
12981     allQuery: '',
12982     /**
12983      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12984      */
12985     mode: 'remote',
12986     /**
12987      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12988      * listWidth has a higher value)
12989      */
12990     minListWidth : 70,
12991     /**
12992      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12993      * allow the user to set arbitrary text into the field (defaults to false)
12994      */
12995     forceSelection:false,
12996     /**
12997      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12998      * if typeAhead = true (defaults to 250)
12999      */
13000     typeAheadDelay : 250,
13001     /**
13002      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13003      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13004      */
13005     valueNotFoundText : undefined,
13006     /**
13007      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13008      */
13009     blockFocus : false,
13010     
13011     /**
13012      * @cfg {Boolean} disableClear Disable showing of clear button.
13013      */
13014     disableClear : false,
13015     /**
13016      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13017      */
13018     alwaysQuery : false,
13019     
13020     /**
13021      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13022      */
13023     multiple : false,
13024     
13025     /**
13026      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13027      */
13028     invalidClass : "has-warning",
13029     
13030     /**
13031      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13032      */
13033     validClass : "has-success",
13034     
13035     /**
13036      * @cfg {Boolean} specialFilter (true|false) special filter default false
13037      */
13038     specialFilter : false,
13039     
13040     /**
13041      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13042      */
13043     mobileTouchView : true,
13044     
13045     /**
13046      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13047      */
13048     useNativeIOS : false,
13049     
13050     /**
13051      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13052      */
13053     mobile_restrict_height : false,
13054     
13055     ios_options : false,
13056     
13057     //private
13058     addicon : false,
13059     editicon: false,
13060     
13061     page: 0,
13062     hasQuery: false,
13063     append: false,
13064     loadNext: false,
13065     autoFocus : true,
13066     tickable : false,
13067     btnPosition : 'right',
13068     triggerList : true,
13069     showToggleBtn : true,
13070     animate : true,
13071     emptyResultText: 'Empty',
13072     triggerText : 'Select',
13073     emptyTitle : '',
13074     
13075     // element that contains real text value.. (when hidden is used..)
13076     
13077     getAutoCreate : function()
13078     {   
13079         var cfg = false;
13080         //render
13081         /*
13082          * Render classic select for iso
13083          */
13084         
13085         if(Roo.isIOS && this.useNativeIOS){
13086             cfg = this.getAutoCreateNativeIOS();
13087             return cfg;
13088         }
13089         
13090         /*
13091          * Touch Devices
13092          */
13093         
13094         if(Roo.isTouch && this.mobileTouchView){
13095             cfg = this.getAutoCreateTouchView();
13096             return cfg;;
13097         }
13098         
13099         /*
13100          *  Normal ComboBox
13101          */
13102         if(!this.tickable){
13103             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13104             return cfg;
13105         }
13106         
13107         /*
13108          *  ComboBox with tickable selections
13109          */
13110              
13111         var align = this.labelAlign || this.parentLabelAlign();
13112         
13113         cfg = {
13114             cls : 'form-group roo-combobox-tickable' //input-group
13115         };
13116         
13117         var btn_text_select = '';
13118         var btn_text_done = '';
13119         var btn_text_cancel = '';
13120         
13121         if (this.btn_text_show) {
13122             btn_text_select = 'Select';
13123             btn_text_done = 'Done';
13124             btn_text_cancel = 'Cancel'; 
13125         }
13126         
13127         var buttons = {
13128             tag : 'div',
13129             cls : 'tickable-buttons',
13130             cn : [
13131                 {
13132                     tag : 'button',
13133                     type : 'button',
13134                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13135                     //html : this.triggerText
13136                     html: btn_text_select
13137                 },
13138                 {
13139                     tag : 'button',
13140                     type : 'button',
13141                     name : 'ok',
13142                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13143                     //html : 'Done'
13144                     html: btn_text_done
13145                 },
13146                 {
13147                     tag : 'button',
13148                     type : 'button',
13149                     name : 'cancel',
13150                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13151                     //html : 'Cancel'
13152                     html: btn_text_cancel
13153                 }
13154             ]
13155         };
13156         
13157         if(this.editable){
13158             buttons.cn.unshift({
13159                 tag: 'input',
13160                 cls: 'roo-select2-search-field-input'
13161             });
13162         }
13163         
13164         var _this = this;
13165         
13166         Roo.each(buttons.cn, function(c){
13167             if (_this.size) {
13168                 c.cls += ' btn-' + _this.size;
13169             }
13170
13171             if (_this.disabled) {
13172                 c.disabled = true;
13173             }
13174         });
13175         
13176         var box = {
13177             tag: 'div',
13178             cn: [
13179                 {
13180                     tag: 'input',
13181                     type : 'hidden',
13182                     cls: 'form-hidden-field'
13183                 },
13184                 {
13185                     tag: 'ul',
13186                     cls: 'roo-select2-choices',
13187                     cn:[
13188                         {
13189                             tag: 'li',
13190                             cls: 'roo-select2-search-field',
13191                             cn: [
13192                                 buttons
13193                             ]
13194                         }
13195                     ]
13196                 }
13197             ]
13198         };
13199         
13200         var combobox = {
13201             cls: 'roo-select2-container input-group roo-select2-container-multi',
13202             cn: [
13203                 box
13204 //                {
13205 //                    tag: 'ul',
13206 //                    cls: 'typeahead typeahead-long dropdown-menu',
13207 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13208 //                }
13209             ]
13210         };
13211         
13212         if(this.hasFeedback && !this.allowBlank){
13213             
13214             var feedback = {
13215                 tag: 'span',
13216                 cls: 'glyphicon form-control-feedback'
13217             };
13218
13219             combobox.cn.push(feedback);
13220         }
13221         
13222         
13223         if (align ==='left' && this.fieldLabel.length) {
13224             
13225             cfg.cls += ' roo-form-group-label-left';
13226             
13227             cfg.cn = [
13228                 {
13229                     tag : 'i',
13230                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13231                     tooltip : 'This field is required'
13232                 },
13233                 {
13234                     tag: 'label',
13235                     'for' :  id,
13236                     cls : 'control-label',
13237                     html : this.fieldLabel
13238
13239                 },
13240                 {
13241                     cls : "", 
13242                     cn: [
13243                         combobox
13244                     ]
13245                 }
13246
13247             ];
13248             
13249             var labelCfg = cfg.cn[1];
13250             var contentCfg = cfg.cn[2];
13251             
13252
13253             if(this.indicatorpos == 'right'){
13254                 
13255                 cfg.cn = [
13256                     {
13257                         tag: 'label',
13258                         'for' :  id,
13259                         cls : 'control-label',
13260                         cn : [
13261                             {
13262                                 tag : 'span',
13263                                 html : this.fieldLabel
13264                             },
13265                             {
13266                                 tag : 'i',
13267                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13268                                 tooltip : 'This field is required'
13269                             }
13270                         ]
13271                     },
13272                     {
13273                         cls : "",
13274                         cn: [
13275                             combobox
13276                         ]
13277                     }
13278
13279                 ];
13280                 
13281                 
13282                 
13283                 labelCfg = cfg.cn[0];
13284                 contentCfg = cfg.cn[1];
13285             
13286             }
13287             
13288             if(this.labelWidth > 12){
13289                 labelCfg.style = "width: " + this.labelWidth + 'px';
13290             }
13291             
13292             if(this.labelWidth < 13 && this.labelmd == 0){
13293                 this.labelmd = this.labelWidth;
13294             }
13295             
13296             if(this.labellg > 0){
13297                 labelCfg.cls += ' col-lg-' + this.labellg;
13298                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13299             }
13300             
13301             if(this.labelmd > 0){
13302                 labelCfg.cls += ' col-md-' + this.labelmd;
13303                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13304             }
13305             
13306             if(this.labelsm > 0){
13307                 labelCfg.cls += ' col-sm-' + this.labelsm;
13308                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13309             }
13310             
13311             if(this.labelxs > 0){
13312                 labelCfg.cls += ' col-xs-' + this.labelxs;
13313                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13314             }
13315                 
13316                 
13317         } else if ( this.fieldLabel.length) {
13318 //                Roo.log(" label");
13319                  cfg.cn = [
13320                     {
13321                         tag : 'i',
13322                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13323                         tooltip : 'This field is required'
13324                     },
13325                     {
13326                         tag: 'label',
13327                         //cls : 'input-group-addon',
13328                         html : this.fieldLabel
13329                     },
13330                     combobox
13331                 ];
13332                 
13333                 if(this.indicatorpos == 'right'){
13334                     cfg.cn = [
13335                         {
13336                             tag: 'label',
13337                             //cls : 'input-group-addon',
13338                             html : this.fieldLabel
13339                         },
13340                         {
13341                             tag : 'i',
13342                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13343                             tooltip : 'This field is required'
13344                         },
13345                         combobox
13346                     ];
13347                     
13348                 }
13349
13350         } else {
13351             
13352 //                Roo.log(" no label && no align");
13353                 cfg = combobox
13354                      
13355                 
13356         }
13357          
13358         var settings=this;
13359         ['xs','sm','md','lg'].map(function(size){
13360             if (settings[size]) {
13361                 cfg.cls += ' col-' + size + '-' + settings[size];
13362             }
13363         });
13364         
13365         return cfg;
13366         
13367     },
13368     
13369     _initEventsCalled : false,
13370     
13371     // private
13372     initEvents: function()
13373     {   
13374         if (this._initEventsCalled) { // as we call render... prevent looping...
13375             return;
13376         }
13377         this._initEventsCalled = true;
13378         
13379         if (!this.store) {
13380             throw "can not find store for combo";
13381         }
13382         
13383         this.indicator = this.indicatorEl();
13384         
13385         this.store = Roo.factory(this.store, Roo.data);
13386         this.store.parent = this;
13387         
13388         // if we are building from html. then this element is so complex, that we can not really
13389         // use the rendered HTML.
13390         // so we have to trash and replace the previous code.
13391         if (Roo.XComponent.build_from_html) {
13392             // remove this element....
13393             var e = this.el.dom, k=0;
13394             while (e ) { e = e.previousSibling;  ++k;}
13395
13396             this.el.remove();
13397             
13398             this.el=false;
13399             this.rendered = false;
13400             
13401             this.render(this.parent().getChildContainer(true), k);
13402         }
13403         
13404         if(Roo.isIOS && this.useNativeIOS){
13405             this.initIOSView();
13406             return;
13407         }
13408         
13409         /*
13410          * Touch Devices
13411          */
13412         
13413         if(Roo.isTouch && this.mobileTouchView){
13414             this.initTouchView();
13415             return;
13416         }
13417         
13418         if(this.tickable){
13419             this.initTickableEvents();
13420             return;
13421         }
13422         
13423         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13424         
13425         if(this.hiddenName){
13426             
13427             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13428             
13429             this.hiddenField.dom.value =
13430                 this.hiddenValue !== undefined ? this.hiddenValue :
13431                 this.value !== undefined ? this.value : '';
13432
13433             // prevent input submission
13434             this.el.dom.removeAttribute('name');
13435             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13436              
13437              
13438         }
13439         //if(Roo.isGecko){
13440         //    this.el.dom.setAttribute('autocomplete', 'off');
13441         //}
13442         
13443         var cls = 'x-combo-list';
13444         
13445         //this.list = new Roo.Layer({
13446         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13447         //});
13448         
13449         var _this = this;
13450         
13451         (function(){
13452             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13453             _this.list.setWidth(lw);
13454         }).defer(100);
13455         
13456         this.list.on('mouseover', this.onViewOver, this);
13457         this.list.on('mousemove', this.onViewMove, this);
13458         this.list.on('scroll', this.onViewScroll, this);
13459         
13460         /*
13461         this.list.swallowEvent('mousewheel');
13462         this.assetHeight = 0;
13463
13464         if(this.title){
13465             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13466             this.assetHeight += this.header.getHeight();
13467         }
13468
13469         this.innerList = this.list.createChild({cls:cls+'-inner'});
13470         this.innerList.on('mouseover', this.onViewOver, this);
13471         this.innerList.on('mousemove', this.onViewMove, this);
13472         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13473         
13474         if(this.allowBlank && !this.pageSize && !this.disableClear){
13475             this.footer = this.list.createChild({cls:cls+'-ft'});
13476             this.pageTb = new Roo.Toolbar(this.footer);
13477            
13478         }
13479         if(this.pageSize){
13480             this.footer = this.list.createChild({cls:cls+'-ft'});
13481             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13482                     {pageSize: this.pageSize});
13483             
13484         }
13485         
13486         if (this.pageTb && this.allowBlank && !this.disableClear) {
13487             var _this = this;
13488             this.pageTb.add(new Roo.Toolbar.Fill(), {
13489                 cls: 'x-btn-icon x-btn-clear',
13490                 text: '&#160;',
13491                 handler: function()
13492                 {
13493                     _this.collapse();
13494                     _this.clearValue();
13495                     _this.onSelect(false, -1);
13496                 }
13497             });
13498         }
13499         if (this.footer) {
13500             this.assetHeight += this.footer.getHeight();
13501         }
13502         */
13503             
13504         if(!this.tpl){
13505             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13506         }
13507
13508         this.view = new Roo.View(this.list, this.tpl, {
13509             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13510         });
13511         //this.view.wrapEl.setDisplayed(false);
13512         this.view.on('click', this.onViewClick, this);
13513         
13514         
13515         this.store.on('beforeload', this.onBeforeLoad, this);
13516         this.store.on('load', this.onLoad, this);
13517         this.store.on('loadexception', this.onLoadException, this);
13518         /*
13519         if(this.resizable){
13520             this.resizer = new Roo.Resizable(this.list,  {
13521                pinned:true, handles:'se'
13522             });
13523             this.resizer.on('resize', function(r, w, h){
13524                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13525                 this.listWidth = w;
13526                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13527                 this.restrictHeight();
13528             }, this);
13529             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13530         }
13531         */
13532         if(!this.editable){
13533             this.editable = true;
13534             this.setEditable(false);
13535         }
13536         
13537         /*
13538         
13539         if (typeof(this.events.add.listeners) != 'undefined') {
13540             
13541             this.addicon = this.wrap.createChild(
13542                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13543        
13544             this.addicon.on('click', function(e) {
13545                 this.fireEvent('add', this);
13546             }, this);
13547         }
13548         if (typeof(this.events.edit.listeners) != 'undefined') {
13549             
13550             this.editicon = this.wrap.createChild(
13551                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13552             if (this.addicon) {
13553                 this.editicon.setStyle('margin-left', '40px');
13554             }
13555             this.editicon.on('click', function(e) {
13556                 
13557                 // we fire even  if inothing is selected..
13558                 this.fireEvent('edit', this, this.lastData );
13559                 
13560             }, this);
13561         }
13562         */
13563         
13564         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13565             "up" : function(e){
13566                 this.inKeyMode = true;
13567                 this.selectPrev();
13568             },
13569
13570             "down" : function(e){
13571                 if(!this.isExpanded()){
13572                     this.onTriggerClick();
13573                 }else{
13574                     this.inKeyMode = true;
13575                     this.selectNext();
13576                 }
13577             },
13578
13579             "enter" : function(e){
13580 //                this.onViewClick();
13581                 //return true;
13582                 this.collapse();
13583                 
13584                 if(this.fireEvent("specialkey", this, e)){
13585                     this.onViewClick(false);
13586                 }
13587                 
13588                 return true;
13589             },
13590
13591             "esc" : function(e){
13592                 this.collapse();
13593             },
13594
13595             "tab" : function(e){
13596                 this.collapse();
13597                 
13598                 if(this.fireEvent("specialkey", this, e)){
13599                     this.onViewClick(false);
13600                 }
13601                 
13602                 return true;
13603             },
13604
13605             scope : this,
13606
13607             doRelay : function(foo, bar, hname){
13608                 if(hname == 'down' || this.scope.isExpanded()){
13609                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13610                 }
13611                 return true;
13612             },
13613
13614             forceKeyDown: true
13615         });
13616         
13617         
13618         this.queryDelay = Math.max(this.queryDelay || 10,
13619                 this.mode == 'local' ? 10 : 250);
13620         
13621         
13622         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13623         
13624         if(this.typeAhead){
13625             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13626         }
13627         if(this.editable !== false){
13628             this.inputEl().on("keyup", this.onKeyUp, this);
13629         }
13630         if(this.forceSelection){
13631             this.inputEl().on('blur', this.doForce, this);
13632         }
13633         
13634         if(this.multiple){
13635             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13636             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13637         }
13638     },
13639     
13640     initTickableEvents: function()
13641     {   
13642         this.createList();
13643         
13644         if(this.hiddenName){
13645             
13646             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13647             
13648             this.hiddenField.dom.value =
13649                 this.hiddenValue !== undefined ? this.hiddenValue :
13650                 this.value !== undefined ? this.value : '';
13651
13652             // prevent input submission
13653             this.el.dom.removeAttribute('name');
13654             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13655              
13656              
13657         }
13658         
13659 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13660         
13661         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13662         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13663         if(this.triggerList){
13664             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13665         }
13666          
13667         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13668         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13669         
13670         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13671         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13672         
13673         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13674         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13675         
13676         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13677         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13678         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13679         
13680         this.okBtn.hide();
13681         this.cancelBtn.hide();
13682         
13683         var _this = this;
13684         
13685         (function(){
13686             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13687             _this.list.setWidth(lw);
13688         }).defer(100);
13689         
13690         this.list.on('mouseover', this.onViewOver, this);
13691         this.list.on('mousemove', this.onViewMove, this);
13692         
13693         this.list.on('scroll', this.onViewScroll, this);
13694         
13695         if(!this.tpl){
13696             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13697                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13698         }
13699
13700         this.view = new Roo.View(this.list, this.tpl, {
13701             singleSelect:true,
13702             tickable:true,
13703             parent:this,
13704             store: this.store,
13705             selectedClass: this.selectedClass
13706         });
13707         
13708         //this.view.wrapEl.setDisplayed(false);
13709         this.view.on('click', this.onViewClick, this);
13710         
13711         
13712         
13713         this.store.on('beforeload', this.onBeforeLoad, this);
13714         this.store.on('load', this.onLoad, this);
13715         this.store.on('loadexception', this.onLoadException, this);
13716         
13717         if(this.editable){
13718             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13719                 "up" : function(e){
13720                     this.inKeyMode = true;
13721                     this.selectPrev();
13722                 },
13723
13724                 "down" : function(e){
13725                     this.inKeyMode = true;
13726                     this.selectNext();
13727                 },
13728
13729                 "enter" : function(e){
13730                     if(this.fireEvent("specialkey", this, e)){
13731                         this.onViewClick(false);
13732                     }
13733                     
13734                     return true;
13735                 },
13736
13737                 "esc" : function(e){
13738                     this.onTickableFooterButtonClick(e, false, false);
13739                 },
13740
13741                 "tab" : function(e){
13742                     this.fireEvent("specialkey", this, e);
13743                     
13744                     this.onTickableFooterButtonClick(e, false, false);
13745                     
13746                     return true;
13747                 },
13748
13749                 scope : this,
13750
13751                 doRelay : function(e, fn, key){
13752                     if(this.scope.isExpanded()){
13753                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13754                     }
13755                     return true;
13756                 },
13757
13758                 forceKeyDown: true
13759             });
13760         }
13761         
13762         this.queryDelay = Math.max(this.queryDelay || 10,
13763                 this.mode == 'local' ? 10 : 250);
13764         
13765         
13766         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13767         
13768         if(this.typeAhead){
13769             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13770         }
13771         
13772         if(this.editable !== false){
13773             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13774         }
13775         
13776         this.indicator = this.indicatorEl();
13777         
13778         if(this.indicator){
13779             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13780             this.indicator.hide();
13781         }
13782         
13783     },
13784
13785     onDestroy : function(){
13786         if(this.view){
13787             this.view.setStore(null);
13788             this.view.el.removeAllListeners();
13789             this.view.el.remove();
13790             this.view.purgeListeners();
13791         }
13792         if(this.list){
13793             this.list.dom.innerHTML  = '';
13794         }
13795         
13796         if(this.store){
13797             this.store.un('beforeload', this.onBeforeLoad, this);
13798             this.store.un('load', this.onLoad, this);
13799             this.store.un('loadexception', this.onLoadException, this);
13800         }
13801         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13802     },
13803
13804     // private
13805     fireKey : function(e){
13806         if(e.isNavKeyPress() && !this.list.isVisible()){
13807             this.fireEvent("specialkey", this, e);
13808         }
13809     },
13810
13811     // private
13812     onResize: function(w, h){
13813 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13814 //        
13815 //        if(typeof w != 'number'){
13816 //            // we do not handle it!?!?
13817 //            return;
13818 //        }
13819 //        var tw = this.trigger.getWidth();
13820 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13821 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13822 //        var x = w - tw;
13823 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13824 //            
13825 //        //this.trigger.setStyle('left', x+'px');
13826 //        
13827 //        if(this.list && this.listWidth === undefined){
13828 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13829 //            this.list.setWidth(lw);
13830 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13831 //        }
13832         
13833     
13834         
13835     },
13836
13837     /**
13838      * Allow or prevent the user from directly editing the field text.  If false is passed,
13839      * the user will only be able to select from the items defined in the dropdown list.  This method
13840      * is the runtime equivalent of setting the 'editable' config option at config time.
13841      * @param {Boolean} value True to allow the user to directly edit the field text
13842      */
13843     setEditable : function(value){
13844         if(value == this.editable){
13845             return;
13846         }
13847         this.editable = value;
13848         if(!value){
13849             this.inputEl().dom.setAttribute('readOnly', true);
13850             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13851             this.inputEl().addClass('x-combo-noedit');
13852         }else{
13853             this.inputEl().dom.setAttribute('readOnly', false);
13854             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13855             this.inputEl().removeClass('x-combo-noedit');
13856         }
13857     },
13858
13859     // private
13860     
13861     onBeforeLoad : function(combo,opts){
13862         if(!this.hasFocus){
13863             return;
13864         }
13865          if (!opts.add) {
13866             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13867          }
13868         this.restrictHeight();
13869         this.selectedIndex = -1;
13870     },
13871
13872     // private
13873     onLoad : function(){
13874         
13875         this.hasQuery = false;
13876         
13877         if(!this.hasFocus){
13878             return;
13879         }
13880         
13881         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13882             this.loading.hide();
13883         }
13884         
13885         if(this.store.getCount() > 0){
13886             
13887             this.expand();
13888             this.restrictHeight();
13889             if(this.lastQuery == this.allQuery){
13890                 if(this.editable && !this.tickable){
13891                     this.inputEl().dom.select();
13892                 }
13893                 
13894                 if(
13895                     !this.selectByValue(this.value, true) &&
13896                     this.autoFocus && 
13897                     (
13898                         !this.store.lastOptions ||
13899                         typeof(this.store.lastOptions.add) == 'undefined' || 
13900                         this.store.lastOptions.add != true
13901                     )
13902                 ){
13903                     this.select(0, true);
13904                 }
13905             }else{
13906                 if(this.autoFocus){
13907                     this.selectNext();
13908                 }
13909                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13910                     this.taTask.delay(this.typeAheadDelay);
13911                 }
13912             }
13913         }else{
13914             this.onEmptyResults();
13915         }
13916         
13917         //this.el.focus();
13918     },
13919     // private
13920     onLoadException : function()
13921     {
13922         this.hasQuery = false;
13923         
13924         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13925             this.loading.hide();
13926         }
13927         
13928         if(this.tickable && this.editable){
13929             return;
13930         }
13931         
13932         this.collapse();
13933         // only causes errors at present
13934         //Roo.log(this.store.reader.jsonData);
13935         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13936             // fixme
13937             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13938         //}
13939         
13940         
13941     },
13942     // private
13943     onTypeAhead : function(){
13944         if(this.store.getCount() > 0){
13945             var r = this.store.getAt(0);
13946             var newValue = r.data[this.displayField];
13947             var len = newValue.length;
13948             var selStart = this.getRawValue().length;
13949             
13950             if(selStart != len){
13951                 this.setRawValue(newValue);
13952                 this.selectText(selStart, newValue.length);
13953             }
13954         }
13955     },
13956
13957     // private
13958     onSelect : function(record, index){
13959         
13960         if(this.fireEvent('beforeselect', this, record, index) !== false){
13961         
13962             this.setFromData(index > -1 ? record.data : false);
13963             
13964             this.collapse();
13965             this.fireEvent('select', this, record, index);
13966         }
13967     },
13968
13969     /**
13970      * Returns the currently selected field value or empty string if no value is set.
13971      * @return {String} value The selected value
13972      */
13973     getValue : function()
13974     {
13975         if(Roo.isIOS && this.useNativeIOS){
13976             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13977         }
13978         
13979         if(this.multiple){
13980             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13981         }
13982         
13983         if(this.valueField){
13984             return typeof this.value != 'undefined' ? this.value : '';
13985         }else{
13986             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13987         }
13988     },
13989     
13990     getRawValue : function()
13991     {
13992         if(Roo.isIOS && this.useNativeIOS){
13993             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13994         }
13995         
13996         var v = this.inputEl().getValue();
13997         
13998         return v;
13999     },
14000
14001     /**
14002      * Clears any text/value currently set in the field
14003      */
14004     clearValue : function(){
14005         
14006         if(this.hiddenField){
14007             this.hiddenField.dom.value = '';
14008         }
14009         this.value = '';
14010         this.setRawValue('');
14011         this.lastSelectionText = '';
14012         this.lastData = false;
14013         
14014         var close = this.closeTriggerEl();
14015         
14016         if(close){
14017             close.hide();
14018         }
14019         
14020         this.validate();
14021         
14022     },
14023
14024     /**
14025      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14026      * will be displayed in the field.  If the value does not match the data value of an existing item,
14027      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14028      * Otherwise the field will be blank (although the value will still be set).
14029      * @param {String} value The value to match
14030      */
14031     setValue : function(v)
14032     {
14033         if(Roo.isIOS && this.useNativeIOS){
14034             this.setIOSValue(v);
14035             return;
14036         }
14037         
14038         if(this.multiple){
14039             this.syncValue();
14040             return;
14041         }
14042         
14043         var text = v;
14044         if(this.valueField){
14045             var r = this.findRecord(this.valueField, v);
14046             if(r){
14047                 text = r.data[this.displayField];
14048             }else if(this.valueNotFoundText !== undefined){
14049                 text = this.valueNotFoundText;
14050             }
14051         }
14052         this.lastSelectionText = text;
14053         if(this.hiddenField){
14054             this.hiddenField.dom.value = v;
14055         }
14056         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14057         this.value = v;
14058         
14059         var close = this.closeTriggerEl();
14060         
14061         if(close){
14062             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14063         }
14064         
14065         this.validate();
14066     },
14067     /**
14068      * @property {Object} the last set data for the element
14069      */
14070     
14071     lastData : false,
14072     /**
14073      * Sets the value of the field based on a object which is related to the record format for the store.
14074      * @param {Object} value the value to set as. or false on reset?
14075      */
14076     setFromData : function(o){
14077         
14078         if(this.multiple){
14079             this.addItem(o);
14080             return;
14081         }
14082             
14083         var dv = ''; // display value
14084         var vv = ''; // value value..
14085         this.lastData = o;
14086         if (this.displayField) {
14087             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14088         } else {
14089             // this is an error condition!!!
14090             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14091         }
14092         
14093         if(this.valueField){
14094             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14095         }
14096         
14097         var close = this.closeTriggerEl();
14098         
14099         if(close){
14100             if(dv.length || vv * 1 > 0){
14101                 close.show() ;
14102                 this.blockFocus=true;
14103             } else {
14104                 close.hide();
14105             }             
14106         }
14107         
14108         if(this.hiddenField){
14109             this.hiddenField.dom.value = vv;
14110             
14111             this.lastSelectionText = dv;
14112             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14113             this.value = vv;
14114             return;
14115         }
14116         // no hidden field.. - we store the value in 'value', but still display
14117         // display field!!!!
14118         this.lastSelectionText = dv;
14119         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14120         this.value = vv;
14121         
14122         
14123         
14124     },
14125     // private
14126     reset : function(){
14127         // overridden so that last data is reset..
14128         
14129         if(this.multiple){
14130             this.clearItem();
14131             return;
14132         }
14133         
14134         this.setValue(this.originalValue);
14135         //this.clearInvalid();
14136         this.lastData = false;
14137         if (this.view) {
14138             this.view.clearSelections();
14139         }
14140         
14141         this.validate();
14142     },
14143     // private
14144     findRecord : function(prop, value){
14145         var record;
14146         if(this.store.getCount() > 0){
14147             this.store.each(function(r){
14148                 if(r.data[prop] == value){
14149                     record = r;
14150                     return false;
14151                 }
14152                 return true;
14153             });
14154         }
14155         return record;
14156     },
14157     
14158     getName: function()
14159     {
14160         // returns hidden if it's set..
14161         if (!this.rendered) {return ''};
14162         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14163         
14164     },
14165     // private
14166     onViewMove : function(e, t){
14167         this.inKeyMode = false;
14168     },
14169
14170     // private
14171     onViewOver : function(e, t){
14172         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14173             return;
14174         }
14175         var item = this.view.findItemFromChild(t);
14176         
14177         if(item){
14178             var index = this.view.indexOf(item);
14179             this.select(index, false);
14180         }
14181     },
14182
14183     // private
14184     onViewClick : function(view, doFocus, el, e)
14185     {
14186         var index = this.view.getSelectedIndexes()[0];
14187         
14188         var r = this.store.getAt(index);
14189         
14190         if(this.tickable){
14191             
14192             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14193                 return;
14194             }
14195             
14196             var rm = false;
14197             var _this = this;
14198             
14199             Roo.each(this.tickItems, function(v,k){
14200                 
14201                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14202                     Roo.log(v);
14203                     _this.tickItems.splice(k, 1);
14204                     
14205                     if(typeof(e) == 'undefined' && view == false){
14206                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14207                     }
14208                     
14209                     rm = true;
14210                     return;
14211                 }
14212             });
14213             
14214             if(rm){
14215                 return;
14216             }
14217             
14218             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14219                 this.tickItems.push(r.data);
14220             }
14221             
14222             if(typeof(e) == 'undefined' && view == false){
14223                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14224             }
14225                     
14226             return;
14227         }
14228         
14229         if(r){
14230             this.onSelect(r, index);
14231         }
14232         if(doFocus !== false && !this.blockFocus){
14233             this.inputEl().focus();
14234         }
14235     },
14236
14237     // private
14238     restrictHeight : function(){
14239         //this.innerList.dom.style.height = '';
14240         //var inner = this.innerList.dom;
14241         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14242         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14243         //this.list.beginUpdate();
14244         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14245         this.list.alignTo(this.inputEl(), this.listAlign);
14246         this.list.alignTo(this.inputEl(), this.listAlign);
14247         //this.list.endUpdate();
14248     },
14249
14250     // private
14251     onEmptyResults : function(){
14252         
14253         if(this.tickable && this.editable){
14254             this.hasFocus = false;
14255             this.restrictHeight();
14256             return;
14257         }
14258         
14259         this.collapse();
14260     },
14261
14262     /**
14263      * Returns true if the dropdown list is expanded, else false.
14264      */
14265     isExpanded : function(){
14266         return this.list.isVisible();
14267     },
14268
14269     /**
14270      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14271      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14272      * @param {String} value The data value of the item to select
14273      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14274      * selected item if it is not currently in view (defaults to true)
14275      * @return {Boolean} True if the value matched an item in the list, else false
14276      */
14277     selectByValue : function(v, scrollIntoView){
14278         if(v !== undefined && v !== null){
14279             var r = this.findRecord(this.valueField || this.displayField, v);
14280             if(r){
14281                 this.select(this.store.indexOf(r), scrollIntoView);
14282                 return true;
14283             }
14284         }
14285         return false;
14286     },
14287
14288     /**
14289      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14290      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14291      * @param {Number} index The zero-based index of the list item to select
14292      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14293      * selected item if it is not currently in view (defaults to true)
14294      */
14295     select : function(index, scrollIntoView){
14296         this.selectedIndex = index;
14297         this.view.select(index);
14298         if(scrollIntoView !== false){
14299             var el = this.view.getNode(index);
14300             /*
14301              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14302              */
14303             if(el){
14304                 this.list.scrollChildIntoView(el, false);
14305             }
14306         }
14307     },
14308
14309     // private
14310     selectNext : function(){
14311         var ct = this.store.getCount();
14312         if(ct > 0){
14313             if(this.selectedIndex == -1){
14314                 this.select(0);
14315             }else if(this.selectedIndex < ct-1){
14316                 this.select(this.selectedIndex+1);
14317             }
14318         }
14319     },
14320
14321     // private
14322     selectPrev : function(){
14323         var ct = this.store.getCount();
14324         if(ct > 0){
14325             if(this.selectedIndex == -1){
14326                 this.select(0);
14327             }else if(this.selectedIndex != 0){
14328                 this.select(this.selectedIndex-1);
14329             }
14330         }
14331     },
14332
14333     // private
14334     onKeyUp : function(e){
14335         if(this.editable !== false && !e.isSpecialKey()){
14336             this.lastKey = e.getKey();
14337             this.dqTask.delay(this.queryDelay);
14338         }
14339     },
14340
14341     // private
14342     validateBlur : function(){
14343         return !this.list || !this.list.isVisible();   
14344     },
14345
14346     // private
14347     initQuery : function(){
14348         
14349         var v = this.getRawValue();
14350         
14351         if(this.tickable && this.editable){
14352             v = this.tickableInputEl().getValue();
14353         }
14354         
14355         this.doQuery(v);
14356     },
14357
14358     // private
14359     doForce : function(){
14360         if(this.inputEl().dom.value.length > 0){
14361             this.inputEl().dom.value =
14362                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14363              
14364         }
14365     },
14366
14367     /**
14368      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14369      * query allowing the query action to be canceled if needed.
14370      * @param {String} query The SQL query to execute
14371      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14372      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14373      * saved in the current store (defaults to false)
14374      */
14375     doQuery : function(q, forceAll){
14376         
14377         if(q === undefined || q === null){
14378             q = '';
14379         }
14380         var qe = {
14381             query: q,
14382             forceAll: forceAll,
14383             combo: this,
14384             cancel:false
14385         };
14386         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14387             return false;
14388         }
14389         q = qe.query;
14390         
14391         forceAll = qe.forceAll;
14392         if(forceAll === true || (q.length >= this.minChars)){
14393             
14394             this.hasQuery = true;
14395             
14396             if(this.lastQuery != q || this.alwaysQuery){
14397                 this.lastQuery = q;
14398                 if(this.mode == 'local'){
14399                     this.selectedIndex = -1;
14400                     if(forceAll){
14401                         this.store.clearFilter();
14402                     }else{
14403                         
14404                         if(this.specialFilter){
14405                             this.fireEvent('specialfilter', this);
14406                             this.onLoad();
14407                             return;
14408                         }
14409                         
14410                         this.store.filter(this.displayField, q);
14411                     }
14412                     
14413                     this.store.fireEvent("datachanged", this.store);
14414                     
14415                     this.onLoad();
14416                     
14417                     
14418                 }else{
14419                     
14420                     this.store.baseParams[this.queryParam] = q;
14421                     
14422                     var options = {params : this.getParams(q)};
14423                     
14424                     if(this.loadNext){
14425                         options.add = true;
14426                         options.params.start = this.page * this.pageSize;
14427                     }
14428                     
14429                     this.store.load(options);
14430                     
14431                     /*
14432                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14433                      *  we should expand the list on onLoad
14434                      *  so command out it
14435                      */
14436 //                    this.expand();
14437                 }
14438             }else{
14439                 this.selectedIndex = -1;
14440                 this.onLoad();   
14441             }
14442         }
14443         
14444         this.loadNext = false;
14445     },
14446     
14447     // private
14448     getParams : function(q){
14449         var p = {};
14450         //p[this.queryParam] = q;
14451         
14452         if(this.pageSize){
14453             p.start = 0;
14454             p.limit = this.pageSize;
14455         }
14456         return p;
14457     },
14458
14459     /**
14460      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14461      */
14462     collapse : function(){
14463         if(!this.isExpanded()){
14464             return;
14465         }
14466         
14467         this.list.hide();
14468         
14469         this.hasFocus = false;
14470         
14471         if(this.tickable){
14472             this.okBtn.hide();
14473             this.cancelBtn.hide();
14474             this.trigger.show();
14475             
14476             if(this.editable){
14477                 this.tickableInputEl().dom.value = '';
14478                 this.tickableInputEl().blur();
14479             }
14480             
14481         }
14482         
14483         Roo.get(document).un('mousedown', this.collapseIf, this);
14484         Roo.get(document).un('mousewheel', this.collapseIf, this);
14485         if (!this.editable) {
14486             Roo.get(document).un('keydown', this.listKeyPress, this);
14487         }
14488         this.fireEvent('collapse', this);
14489         
14490         this.validate();
14491     },
14492
14493     // private
14494     collapseIf : function(e){
14495         var in_combo  = e.within(this.el);
14496         var in_list =  e.within(this.list);
14497         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14498         
14499         if (in_combo || in_list || is_list) {
14500             //e.stopPropagation();
14501             return;
14502         }
14503         
14504         if(this.tickable){
14505             this.onTickableFooterButtonClick(e, false, false);
14506         }
14507
14508         this.collapse();
14509         
14510     },
14511
14512     /**
14513      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14514      */
14515     expand : function(){
14516        
14517         if(this.isExpanded() || !this.hasFocus){
14518             return;
14519         }
14520         
14521         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14522         this.list.setWidth(lw);
14523         
14524         Roo.log('expand');
14525         
14526         this.list.show();
14527         
14528         this.restrictHeight();
14529         
14530         if(this.tickable){
14531             
14532             this.tickItems = Roo.apply([], this.item);
14533             
14534             this.okBtn.show();
14535             this.cancelBtn.show();
14536             this.trigger.hide();
14537             
14538             if(this.editable){
14539                 this.tickableInputEl().focus();
14540             }
14541             
14542         }
14543         
14544         Roo.get(document).on('mousedown', this.collapseIf, this);
14545         Roo.get(document).on('mousewheel', this.collapseIf, this);
14546         if (!this.editable) {
14547             Roo.get(document).on('keydown', this.listKeyPress, this);
14548         }
14549         
14550         this.fireEvent('expand', this);
14551     },
14552
14553     // private
14554     // Implements the default empty TriggerField.onTriggerClick function
14555     onTriggerClick : function(e)
14556     {
14557         Roo.log('trigger click');
14558         
14559         if(this.disabled || !this.triggerList){
14560             return;
14561         }
14562         
14563         this.page = 0;
14564         this.loadNext = false;
14565         
14566         if(this.isExpanded()){
14567             this.collapse();
14568             if (!this.blockFocus) {
14569                 this.inputEl().focus();
14570             }
14571             
14572         }else {
14573             this.hasFocus = true;
14574             if(this.triggerAction == 'all') {
14575                 this.doQuery(this.allQuery, true);
14576             } else {
14577                 this.doQuery(this.getRawValue());
14578             }
14579             if (!this.blockFocus) {
14580                 this.inputEl().focus();
14581             }
14582         }
14583     },
14584     
14585     onTickableTriggerClick : function(e)
14586     {
14587         if(this.disabled){
14588             return;
14589         }
14590         
14591         this.page = 0;
14592         this.loadNext = false;
14593         this.hasFocus = true;
14594         
14595         if(this.triggerAction == 'all') {
14596             this.doQuery(this.allQuery, true);
14597         } else {
14598             this.doQuery(this.getRawValue());
14599         }
14600     },
14601     
14602     onSearchFieldClick : function(e)
14603     {
14604         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14605             this.onTickableFooterButtonClick(e, false, false);
14606             return;
14607         }
14608         
14609         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14610             return;
14611         }
14612         
14613         this.page = 0;
14614         this.loadNext = false;
14615         this.hasFocus = true;
14616         
14617         if(this.triggerAction == 'all') {
14618             this.doQuery(this.allQuery, true);
14619         } else {
14620             this.doQuery(this.getRawValue());
14621         }
14622     },
14623     
14624     listKeyPress : function(e)
14625     {
14626         //Roo.log('listkeypress');
14627         // scroll to first matching element based on key pres..
14628         if (e.isSpecialKey()) {
14629             return false;
14630         }
14631         var k = String.fromCharCode(e.getKey()).toUpperCase();
14632         //Roo.log(k);
14633         var match  = false;
14634         var csel = this.view.getSelectedNodes();
14635         var cselitem = false;
14636         if (csel.length) {
14637             var ix = this.view.indexOf(csel[0]);
14638             cselitem  = this.store.getAt(ix);
14639             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14640                 cselitem = false;
14641             }
14642             
14643         }
14644         
14645         this.store.each(function(v) { 
14646             if (cselitem) {
14647                 // start at existing selection.
14648                 if (cselitem.id == v.id) {
14649                     cselitem = false;
14650                 }
14651                 return true;
14652             }
14653                 
14654             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14655                 match = this.store.indexOf(v);
14656                 return false;
14657             }
14658             return true;
14659         }, this);
14660         
14661         if (match === false) {
14662             return true; // no more action?
14663         }
14664         // scroll to?
14665         this.view.select(match);
14666         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14667         sn.scrollIntoView(sn.dom.parentNode, false);
14668     },
14669     
14670     onViewScroll : function(e, t){
14671         
14672         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){
14673             return;
14674         }
14675         
14676         this.hasQuery = true;
14677         
14678         this.loading = this.list.select('.loading', true).first();
14679         
14680         if(this.loading === null){
14681             this.list.createChild({
14682                 tag: 'div',
14683                 cls: 'loading roo-select2-more-results roo-select2-active',
14684                 html: 'Loading more results...'
14685             });
14686             
14687             this.loading = this.list.select('.loading', true).first();
14688             
14689             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14690             
14691             this.loading.hide();
14692         }
14693         
14694         this.loading.show();
14695         
14696         var _combo = this;
14697         
14698         this.page++;
14699         this.loadNext = true;
14700         
14701         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14702         
14703         return;
14704     },
14705     
14706     addItem : function(o)
14707     {   
14708         var dv = ''; // display value
14709         
14710         if (this.displayField) {
14711             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14712         } else {
14713             // this is an error condition!!!
14714             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14715         }
14716         
14717         if(!dv.length){
14718             return;
14719         }
14720         
14721         var choice = this.choices.createChild({
14722             tag: 'li',
14723             cls: 'roo-select2-search-choice',
14724             cn: [
14725                 {
14726                     tag: 'div',
14727                     html: dv
14728                 },
14729                 {
14730                     tag: 'a',
14731                     href: '#',
14732                     cls: 'roo-select2-search-choice-close fa fa-times',
14733                     tabindex: '-1'
14734                 }
14735             ]
14736             
14737         }, this.searchField);
14738         
14739         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14740         
14741         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14742         
14743         this.item.push(o);
14744         
14745         this.lastData = o;
14746         
14747         this.syncValue();
14748         
14749         this.inputEl().dom.value = '';
14750         
14751         this.validate();
14752     },
14753     
14754     onRemoveItem : function(e, _self, o)
14755     {
14756         e.preventDefault();
14757         
14758         this.lastItem = Roo.apply([], this.item);
14759         
14760         var index = this.item.indexOf(o.data) * 1;
14761         
14762         if( index < 0){
14763             Roo.log('not this item?!');
14764             return;
14765         }
14766         
14767         this.item.splice(index, 1);
14768         o.item.remove();
14769         
14770         this.syncValue();
14771         
14772         this.fireEvent('remove', this, e);
14773         
14774         this.validate();
14775         
14776     },
14777     
14778     syncValue : function()
14779     {
14780         if(!this.item.length){
14781             this.clearValue();
14782             return;
14783         }
14784             
14785         var value = [];
14786         var _this = this;
14787         Roo.each(this.item, function(i){
14788             if(_this.valueField){
14789                 value.push(i[_this.valueField]);
14790                 return;
14791             }
14792
14793             value.push(i);
14794         });
14795
14796         this.value = value.join(',');
14797
14798         if(this.hiddenField){
14799             this.hiddenField.dom.value = this.value;
14800         }
14801         
14802         this.store.fireEvent("datachanged", this.store);
14803         
14804         this.validate();
14805     },
14806     
14807     clearItem : function()
14808     {
14809         if(!this.multiple){
14810             return;
14811         }
14812         
14813         this.item = [];
14814         
14815         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14816            c.remove();
14817         });
14818         
14819         this.syncValue();
14820         
14821         this.validate();
14822         
14823         if(this.tickable && !Roo.isTouch){
14824             this.view.refresh();
14825         }
14826     },
14827     
14828     inputEl: function ()
14829     {
14830         if(Roo.isIOS && this.useNativeIOS){
14831             return this.el.select('select.roo-ios-select', true).first();
14832         }
14833         
14834         if(Roo.isTouch && this.mobileTouchView){
14835             return this.el.select('input.form-control',true).first();
14836         }
14837         
14838         if(this.tickable){
14839             return this.searchField;
14840         }
14841         
14842         return this.el.select('input.form-control',true).first();
14843     },
14844     
14845     onTickableFooterButtonClick : function(e, btn, el)
14846     {
14847         e.preventDefault();
14848         
14849         this.lastItem = Roo.apply([], this.item);
14850         
14851         if(btn && btn.name == 'cancel'){
14852             this.tickItems = Roo.apply([], this.item);
14853             this.collapse();
14854             return;
14855         }
14856         
14857         this.clearItem();
14858         
14859         var _this = this;
14860         
14861         Roo.each(this.tickItems, function(o){
14862             _this.addItem(o);
14863         });
14864         
14865         this.collapse();
14866         
14867     },
14868     
14869     validate : function()
14870     {
14871         if(this.getVisibilityEl().hasClass('hidden')){
14872             return true;
14873         }
14874         
14875         var v = this.getRawValue();
14876         
14877         if(this.multiple){
14878             v = this.getValue();
14879         }
14880         
14881         if(this.disabled || this.allowBlank || v.length){
14882             this.markValid();
14883             return true;
14884         }
14885         
14886         this.markInvalid();
14887         return false;
14888     },
14889     
14890     tickableInputEl : function()
14891     {
14892         if(!this.tickable || !this.editable){
14893             return this.inputEl();
14894         }
14895         
14896         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14897     },
14898     
14899     
14900     getAutoCreateTouchView : function()
14901     {
14902         var id = Roo.id();
14903         
14904         var cfg = {
14905             cls: 'form-group' //input-group
14906         };
14907         
14908         var input =  {
14909             tag: 'input',
14910             id : id,
14911             type : this.inputType,
14912             cls : 'form-control x-combo-noedit',
14913             autocomplete: 'new-password',
14914             placeholder : this.placeholder || '',
14915             readonly : true
14916         };
14917         
14918         if (this.name) {
14919             input.name = this.name;
14920         }
14921         
14922         if (this.size) {
14923             input.cls += ' input-' + this.size;
14924         }
14925         
14926         if (this.disabled) {
14927             input.disabled = true;
14928         }
14929         
14930         var inputblock = {
14931             cls : '',
14932             cn : [
14933                 input
14934             ]
14935         };
14936         
14937         if(this.before){
14938             inputblock.cls += ' input-group';
14939             
14940             inputblock.cn.unshift({
14941                 tag :'span',
14942                 cls : 'input-group-addon',
14943                 html : this.before
14944             });
14945         }
14946         
14947         if(this.removable && !this.multiple){
14948             inputblock.cls += ' roo-removable';
14949             
14950             inputblock.cn.push({
14951                 tag: 'button',
14952                 html : 'x',
14953                 cls : 'roo-combo-removable-btn close'
14954             });
14955         }
14956
14957         if(this.hasFeedback && !this.allowBlank){
14958             
14959             inputblock.cls += ' has-feedback';
14960             
14961             inputblock.cn.push({
14962                 tag: 'span',
14963                 cls: 'glyphicon form-control-feedback'
14964             });
14965             
14966         }
14967         
14968         if (this.after) {
14969             
14970             inputblock.cls += (this.before) ? '' : ' input-group';
14971             
14972             inputblock.cn.push({
14973                 tag :'span',
14974                 cls : 'input-group-addon',
14975                 html : this.after
14976             });
14977         }
14978
14979         var box = {
14980             tag: 'div',
14981             cn: [
14982                 {
14983                     tag: 'input',
14984                     type : 'hidden',
14985                     cls: 'form-hidden-field'
14986                 },
14987                 inputblock
14988             ]
14989             
14990         };
14991         
14992         if(this.multiple){
14993             box = {
14994                 tag: 'div',
14995                 cn: [
14996                     {
14997                         tag: 'input',
14998                         type : 'hidden',
14999                         cls: 'form-hidden-field'
15000                     },
15001                     {
15002                         tag: 'ul',
15003                         cls: 'roo-select2-choices',
15004                         cn:[
15005                             {
15006                                 tag: 'li',
15007                                 cls: 'roo-select2-search-field',
15008                                 cn: [
15009
15010                                     inputblock
15011                                 ]
15012                             }
15013                         ]
15014                     }
15015                 ]
15016             }
15017         };
15018         
15019         var combobox = {
15020             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15021             cn: [
15022                 box
15023             ]
15024         };
15025         
15026         if(!this.multiple && this.showToggleBtn){
15027             
15028             var caret = {
15029                         tag: 'span',
15030                         cls: 'caret'
15031             };
15032             
15033             if (this.caret != false) {
15034                 caret = {
15035                      tag: 'i',
15036                      cls: 'fa fa-' + this.caret
15037                 };
15038                 
15039             }
15040             
15041             combobox.cn.push({
15042                 tag :'span',
15043                 cls : 'input-group-addon btn dropdown-toggle',
15044                 cn : [
15045                     caret,
15046                     {
15047                         tag: 'span',
15048                         cls: 'combobox-clear',
15049                         cn  : [
15050                             {
15051                                 tag : 'i',
15052                                 cls: 'icon-remove'
15053                             }
15054                         ]
15055                     }
15056                 ]
15057
15058             })
15059         }
15060         
15061         if(this.multiple){
15062             combobox.cls += ' roo-select2-container-multi';
15063         }
15064         
15065         var align = this.labelAlign || this.parentLabelAlign();
15066         
15067         if (align ==='left' && this.fieldLabel.length) {
15068
15069             cfg.cn = [
15070                 {
15071                    tag : 'i',
15072                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15073                    tooltip : 'This field is required'
15074                 },
15075                 {
15076                     tag: 'label',
15077                     cls : 'control-label',
15078                     html : this.fieldLabel
15079
15080                 },
15081                 {
15082                     cls : '', 
15083                     cn: [
15084                         combobox
15085                     ]
15086                 }
15087             ];
15088             
15089             var labelCfg = cfg.cn[1];
15090             var contentCfg = cfg.cn[2];
15091             
15092
15093             if(this.indicatorpos == 'right'){
15094                 cfg.cn = [
15095                     {
15096                         tag: 'label',
15097                         'for' :  id,
15098                         cls : 'control-label',
15099                         cn : [
15100                             {
15101                                 tag : 'span',
15102                                 html : this.fieldLabel
15103                             },
15104                             {
15105                                 tag : 'i',
15106                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15107                                 tooltip : 'This field is required'
15108                             }
15109                         ]
15110                     },
15111                     {
15112                         cls : "",
15113                         cn: [
15114                             combobox
15115                         ]
15116                     }
15117
15118                 ];
15119                 
15120                 labelCfg = cfg.cn[0];
15121                 contentCfg = cfg.cn[1];
15122             }
15123             
15124            
15125             
15126             if(this.labelWidth > 12){
15127                 labelCfg.style = "width: " + this.labelWidth + 'px';
15128             }
15129             
15130             if(this.labelWidth < 13 && this.labelmd == 0){
15131                 this.labelmd = this.labelWidth;
15132             }
15133             
15134             if(this.labellg > 0){
15135                 labelCfg.cls += ' col-lg-' + this.labellg;
15136                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15137             }
15138             
15139             if(this.labelmd > 0){
15140                 labelCfg.cls += ' col-md-' + this.labelmd;
15141                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15142             }
15143             
15144             if(this.labelsm > 0){
15145                 labelCfg.cls += ' col-sm-' + this.labelsm;
15146                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15147             }
15148             
15149             if(this.labelxs > 0){
15150                 labelCfg.cls += ' col-xs-' + this.labelxs;
15151                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15152             }
15153                 
15154                 
15155         } else if ( this.fieldLabel.length) {
15156             cfg.cn = [
15157                 {
15158                    tag : 'i',
15159                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15160                    tooltip : 'This field is required'
15161                 },
15162                 {
15163                     tag: 'label',
15164                     cls : 'control-label',
15165                     html : this.fieldLabel
15166
15167                 },
15168                 {
15169                     cls : '', 
15170                     cn: [
15171                         combobox
15172                     ]
15173                 }
15174             ];
15175             
15176             if(this.indicatorpos == 'right'){
15177                 cfg.cn = [
15178                     {
15179                         tag: 'label',
15180                         cls : 'control-label',
15181                         html : this.fieldLabel,
15182                         cn : [
15183                             {
15184                                tag : 'i',
15185                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15186                                tooltip : 'This field is required'
15187                             }
15188                         ]
15189                     },
15190                     {
15191                         cls : '', 
15192                         cn: [
15193                             combobox
15194                         ]
15195                     }
15196                 ];
15197             }
15198         } else {
15199             cfg.cn = combobox;    
15200         }
15201         
15202         
15203         var settings = this;
15204         
15205         ['xs','sm','md','lg'].map(function(size){
15206             if (settings[size]) {
15207                 cfg.cls += ' col-' + size + '-' + settings[size];
15208             }
15209         });
15210         
15211         return cfg;
15212     },
15213     
15214     initTouchView : function()
15215     {
15216         this.renderTouchView();
15217         
15218         this.touchViewEl.on('scroll', function(){
15219             this.el.dom.scrollTop = 0;
15220         }, this);
15221         
15222         this.originalValue = this.getValue();
15223         
15224         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15225         
15226         this.inputEl().on("click", this.showTouchView, this);
15227         if (this.triggerEl) {
15228             this.triggerEl.on("click", this.showTouchView, this);
15229         }
15230         
15231         
15232         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15233         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15234         
15235         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15236         
15237         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15238         this.store.on('load', this.onTouchViewLoad, this);
15239         this.store.on('loadexception', this.onTouchViewLoadException, this);
15240         
15241         if(this.hiddenName){
15242             
15243             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15244             
15245             this.hiddenField.dom.value =
15246                 this.hiddenValue !== undefined ? this.hiddenValue :
15247                 this.value !== undefined ? this.value : '';
15248         
15249             this.el.dom.removeAttribute('name');
15250             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15251         }
15252         
15253         if(this.multiple){
15254             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15255             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15256         }
15257         
15258         if(this.removable && !this.multiple){
15259             var close = this.closeTriggerEl();
15260             if(close){
15261                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15262                 close.on('click', this.removeBtnClick, this, close);
15263             }
15264         }
15265         /*
15266          * fix the bug in Safari iOS8
15267          */
15268         this.inputEl().on("focus", function(e){
15269             document.activeElement.blur();
15270         }, this);
15271         
15272         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15273         
15274         return;
15275         
15276         
15277     },
15278     
15279     renderTouchView : function()
15280     {
15281         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15282         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15283         
15284         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15285         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15286         
15287         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15288         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15289         this.touchViewBodyEl.setStyle('overflow', 'auto');
15290         
15291         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15292         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15293         
15294         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15295         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15296         
15297     },
15298     
15299     showTouchView : function()
15300     {
15301         if(this.disabled){
15302             return;
15303         }
15304         
15305         this.touchViewHeaderEl.hide();
15306
15307         if(this.modalTitle.length){
15308             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15309             this.touchViewHeaderEl.show();
15310         }
15311
15312         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15313         this.touchViewEl.show();
15314
15315         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15316         
15317         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15318         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15319
15320         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15321
15322         if(this.modalTitle.length){
15323             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15324         }
15325         
15326         this.touchViewBodyEl.setHeight(bodyHeight);
15327
15328         if(this.animate){
15329             var _this = this;
15330             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15331         }else{
15332             this.touchViewEl.addClass('in');
15333         }
15334         
15335         if(this._touchViewMask){
15336             Roo.get(document.body).addClass("x-body-masked");
15337             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15338             this._touchViewMask.setStyle('z-index', 10000);
15339             this._touchViewMask.addClass('show');
15340         }
15341         
15342         this.doTouchViewQuery();
15343         
15344     },
15345     
15346     hideTouchView : function()
15347     {
15348         this.touchViewEl.removeClass('in');
15349
15350         if(this.animate){
15351             var _this = this;
15352             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15353         }else{
15354             this.touchViewEl.setStyle('display', 'none');
15355         }
15356         
15357         if(this._touchViewMask){
15358             this._touchViewMask.removeClass('show');
15359             Roo.get(document.body).removeClass("x-body-masked");
15360         }
15361     },
15362     
15363     setTouchViewValue : function()
15364     {
15365         if(this.multiple){
15366             this.clearItem();
15367         
15368             var _this = this;
15369
15370             Roo.each(this.tickItems, function(o){
15371                 this.addItem(o);
15372             }, this);
15373         }
15374         
15375         this.hideTouchView();
15376     },
15377     
15378     doTouchViewQuery : function()
15379     {
15380         var qe = {
15381             query: '',
15382             forceAll: true,
15383             combo: this,
15384             cancel:false
15385         };
15386         
15387         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15388             return false;
15389         }
15390         
15391         if(!this.alwaysQuery || this.mode == 'local'){
15392             this.onTouchViewLoad();
15393             return;
15394         }
15395         
15396         this.store.load();
15397     },
15398     
15399     onTouchViewBeforeLoad : function(combo,opts)
15400     {
15401         return;
15402     },
15403
15404     // private
15405     onTouchViewLoad : function()
15406     {
15407         if(this.store.getCount() < 1){
15408             this.onTouchViewEmptyResults();
15409             return;
15410         }
15411         
15412         this.clearTouchView();
15413         
15414         var rawValue = this.getRawValue();
15415         
15416         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15417         
15418         this.tickItems = [];
15419         
15420         this.store.data.each(function(d, rowIndex){
15421             var row = this.touchViewListGroup.createChild(template);
15422             
15423             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15424                 row.addClass(d.data.cls);
15425             }
15426             
15427             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15428                 var cfg = {
15429                     data : d.data,
15430                     html : d.data[this.displayField]
15431                 };
15432                 
15433                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15434                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15435                 }
15436             }
15437             row.removeClass('selected');
15438             if(!this.multiple && this.valueField &&
15439                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15440             {
15441                 // radio buttons..
15442                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15443                 row.addClass('selected');
15444             }
15445             
15446             if(this.multiple && this.valueField &&
15447                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15448             {
15449                 
15450                 // checkboxes...
15451                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15452                 this.tickItems.push(d.data);
15453             }
15454             
15455             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15456             
15457         }, this);
15458         
15459         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15460         
15461         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15462
15463         if(this.modalTitle.length){
15464             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15465         }
15466
15467         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15468         
15469         if(this.mobile_restrict_height && listHeight < bodyHeight){
15470             this.touchViewBodyEl.setHeight(listHeight);
15471         }
15472         
15473         var _this = this;
15474         
15475         if(firstChecked && listHeight > bodyHeight){
15476             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15477         }
15478         
15479     },
15480     
15481     onTouchViewLoadException : function()
15482     {
15483         this.hideTouchView();
15484     },
15485     
15486     onTouchViewEmptyResults : function()
15487     {
15488         this.clearTouchView();
15489         
15490         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15491         
15492         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15493         
15494     },
15495     
15496     clearTouchView : function()
15497     {
15498         this.touchViewListGroup.dom.innerHTML = '';
15499     },
15500     
15501     onTouchViewClick : function(e, el, o)
15502     {
15503         e.preventDefault();
15504         
15505         var row = o.row;
15506         var rowIndex = o.rowIndex;
15507         
15508         var r = this.store.getAt(rowIndex);
15509         
15510         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15511             
15512             if(!this.multiple){
15513                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15514                     c.dom.removeAttribute('checked');
15515                 }, this);
15516
15517                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15518
15519                 this.setFromData(r.data);
15520
15521                 var close = this.closeTriggerEl();
15522
15523                 if(close){
15524                     close.show();
15525                 }
15526
15527                 this.hideTouchView();
15528
15529                 this.fireEvent('select', this, r, rowIndex);
15530
15531                 return;
15532             }
15533
15534             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15535                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15536                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15537                 return;
15538             }
15539
15540             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15541             this.addItem(r.data);
15542             this.tickItems.push(r.data);
15543         }
15544     },
15545     
15546     getAutoCreateNativeIOS : function()
15547     {
15548         var cfg = {
15549             cls: 'form-group' //input-group,
15550         };
15551         
15552         var combobox =  {
15553             tag: 'select',
15554             cls : 'roo-ios-select'
15555         };
15556         
15557         if (this.name) {
15558             combobox.name = this.name;
15559         }
15560         
15561         if (this.disabled) {
15562             combobox.disabled = true;
15563         }
15564         
15565         var settings = this;
15566         
15567         ['xs','sm','md','lg'].map(function(size){
15568             if (settings[size]) {
15569                 cfg.cls += ' col-' + size + '-' + settings[size];
15570             }
15571         });
15572         
15573         cfg.cn = combobox;
15574         
15575         return cfg;
15576         
15577     },
15578     
15579     initIOSView : function()
15580     {
15581         this.store.on('load', this.onIOSViewLoad, this);
15582         
15583         return;
15584     },
15585     
15586     onIOSViewLoad : function()
15587     {
15588         if(this.store.getCount() < 1){
15589             return;
15590         }
15591         
15592         this.clearIOSView();
15593         
15594         if(this.allowBlank) {
15595             
15596             var default_text = '-- SELECT --';
15597             
15598             if(this.placeholder.length){
15599                 default_text = this.placeholder;
15600             }
15601             
15602             if(this.emptyTitle.length){
15603                 default_text += ' - ' + this.emptyTitle + ' -';
15604             }
15605             
15606             var opt = this.inputEl().createChild({
15607                 tag: 'option',
15608                 value : 0,
15609                 html : default_text
15610             });
15611             
15612             var o = {};
15613             o[this.valueField] = 0;
15614             o[this.displayField] = default_text;
15615             
15616             this.ios_options.push({
15617                 data : o,
15618                 el : opt
15619             });
15620             
15621         }
15622         
15623         this.store.data.each(function(d, rowIndex){
15624             
15625             var html = '';
15626             
15627             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15628                 html = d.data[this.displayField];
15629             }
15630             
15631             var value = '';
15632             
15633             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15634                 value = d.data[this.valueField];
15635             }
15636             
15637             var option = {
15638                 tag: 'option',
15639                 value : value,
15640                 html : html
15641             };
15642             
15643             if(this.value == d.data[this.valueField]){
15644                 option['selected'] = true;
15645             }
15646             
15647             var opt = this.inputEl().createChild(option);
15648             
15649             this.ios_options.push({
15650                 data : d.data,
15651                 el : opt
15652             });
15653             
15654         }, this);
15655         
15656         this.inputEl().on('change', function(){
15657            this.fireEvent('select', this);
15658         }, this);
15659         
15660     },
15661     
15662     clearIOSView: function()
15663     {
15664         this.inputEl().dom.innerHTML = '';
15665         
15666         this.ios_options = [];
15667     },
15668     
15669     setIOSValue: function(v)
15670     {
15671         this.value = v;
15672         
15673         if(!this.ios_options){
15674             return;
15675         }
15676         
15677         Roo.each(this.ios_options, function(opts){
15678            
15679            opts.el.dom.removeAttribute('selected');
15680            
15681            if(opts.data[this.valueField] != v){
15682                return;
15683            }
15684            
15685            opts.el.dom.setAttribute('selected', true);
15686            
15687         }, this);
15688     }
15689
15690     /** 
15691     * @cfg {Boolean} grow 
15692     * @hide 
15693     */
15694     /** 
15695     * @cfg {Number} growMin 
15696     * @hide 
15697     */
15698     /** 
15699     * @cfg {Number} growMax 
15700     * @hide 
15701     */
15702     /**
15703      * @hide
15704      * @method autoSize
15705      */
15706 });
15707
15708 Roo.apply(Roo.bootstrap.ComboBox,  {
15709     
15710     header : {
15711         tag: 'div',
15712         cls: 'modal-header',
15713         cn: [
15714             {
15715                 tag: 'h4',
15716                 cls: 'modal-title'
15717             }
15718         ]
15719     },
15720     
15721     body : {
15722         tag: 'div',
15723         cls: 'modal-body',
15724         cn: [
15725             {
15726                 tag: 'ul',
15727                 cls: 'list-group'
15728             }
15729         ]
15730     },
15731     
15732     listItemRadio : {
15733         tag: 'li',
15734         cls: 'list-group-item',
15735         cn: [
15736             {
15737                 tag: 'span',
15738                 cls: 'roo-combobox-list-group-item-value'
15739             },
15740             {
15741                 tag: 'div',
15742                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15743                 cn: [
15744                     {
15745                         tag: 'input',
15746                         type: 'radio'
15747                     },
15748                     {
15749                         tag: 'label'
15750                     }
15751                 ]
15752             }
15753         ]
15754     },
15755     
15756     listItemCheckbox : {
15757         tag: 'li',
15758         cls: 'list-group-item',
15759         cn: [
15760             {
15761                 tag: 'span',
15762                 cls: 'roo-combobox-list-group-item-value'
15763             },
15764             {
15765                 tag: 'div',
15766                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15767                 cn: [
15768                     {
15769                         tag: 'input',
15770                         type: 'checkbox'
15771                     },
15772                     {
15773                         tag: 'label'
15774                     }
15775                 ]
15776             }
15777         ]
15778     },
15779     
15780     emptyResult : {
15781         tag: 'div',
15782         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15783     },
15784     
15785     footer : {
15786         tag: 'div',
15787         cls: 'modal-footer',
15788         cn: [
15789             {
15790                 tag: 'div',
15791                 cls: 'row',
15792                 cn: [
15793                     {
15794                         tag: 'div',
15795                         cls: 'col-xs-6 text-left',
15796                         cn: {
15797                             tag: 'button',
15798                             cls: 'btn btn-danger roo-touch-view-cancel',
15799                             html: 'Cancel'
15800                         }
15801                     },
15802                     {
15803                         tag: 'div',
15804                         cls: 'col-xs-6 text-right',
15805                         cn: {
15806                             tag: 'button',
15807                             cls: 'btn btn-success roo-touch-view-ok',
15808                             html: 'OK'
15809                         }
15810                     }
15811                 ]
15812             }
15813         ]
15814         
15815     }
15816 });
15817
15818 Roo.apply(Roo.bootstrap.ComboBox,  {
15819     
15820     touchViewTemplate : {
15821         tag: 'div',
15822         cls: 'modal fade roo-combobox-touch-view',
15823         cn: [
15824             {
15825                 tag: 'div',
15826                 cls: 'modal-dialog',
15827                 style : 'position:fixed', // we have to fix position....
15828                 cn: [
15829                     {
15830                         tag: 'div',
15831                         cls: 'modal-content',
15832                         cn: [
15833                             Roo.bootstrap.ComboBox.header,
15834                             Roo.bootstrap.ComboBox.body,
15835                             Roo.bootstrap.ComboBox.footer
15836                         ]
15837                     }
15838                 ]
15839             }
15840         ]
15841     }
15842 });/*
15843  * Based on:
15844  * Ext JS Library 1.1.1
15845  * Copyright(c) 2006-2007, Ext JS, LLC.
15846  *
15847  * Originally Released Under LGPL - original licence link has changed is not relivant.
15848  *
15849  * Fork - LGPL
15850  * <script type="text/javascript">
15851  */
15852
15853 /**
15854  * @class Roo.View
15855  * @extends Roo.util.Observable
15856  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15857  * This class also supports single and multi selection modes. <br>
15858  * Create a data model bound view:
15859  <pre><code>
15860  var store = new Roo.data.Store(...);
15861
15862  var view = new Roo.View({
15863     el : "my-element",
15864     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15865  
15866     singleSelect: true,
15867     selectedClass: "ydataview-selected",
15868     store: store
15869  });
15870
15871  // listen for node click?
15872  view.on("click", function(vw, index, node, e){
15873  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15874  });
15875
15876  // load XML data
15877  dataModel.load("foobar.xml");
15878  </code></pre>
15879  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15880  * <br><br>
15881  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15882  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15883  * 
15884  * Note: old style constructor is still suported (container, template, config)
15885  * 
15886  * @constructor
15887  * Create a new View
15888  * @param {Object} config The config object
15889  * 
15890  */
15891 Roo.View = function(config, depreciated_tpl, depreciated_config){
15892     
15893     this.parent = false;
15894     
15895     if (typeof(depreciated_tpl) == 'undefined') {
15896         // new way.. - universal constructor.
15897         Roo.apply(this, config);
15898         this.el  = Roo.get(this.el);
15899     } else {
15900         // old format..
15901         this.el  = Roo.get(config);
15902         this.tpl = depreciated_tpl;
15903         Roo.apply(this, depreciated_config);
15904     }
15905     this.wrapEl  = this.el.wrap().wrap();
15906     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15907     
15908     
15909     if(typeof(this.tpl) == "string"){
15910         this.tpl = new Roo.Template(this.tpl);
15911     } else {
15912         // support xtype ctors..
15913         this.tpl = new Roo.factory(this.tpl, Roo);
15914     }
15915     
15916     
15917     this.tpl.compile();
15918     
15919     /** @private */
15920     this.addEvents({
15921         /**
15922          * @event beforeclick
15923          * Fires before a click is processed. Returns false to cancel the default action.
15924          * @param {Roo.View} this
15925          * @param {Number} index The index of the target node
15926          * @param {HTMLElement} node The target node
15927          * @param {Roo.EventObject} e The raw event object
15928          */
15929             "beforeclick" : true,
15930         /**
15931          * @event click
15932          * Fires when a template node is clicked.
15933          * @param {Roo.View} this
15934          * @param {Number} index The index of the target node
15935          * @param {HTMLElement} node The target node
15936          * @param {Roo.EventObject} e The raw event object
15937          */
15938             "click" : true,
15939         /**
15940          * @event dblclick
15941          * Fires when a template node is double clicked.
15942          * @param {Roo.View} this
15943          * @param {Number} index The index of the target node
15944          * @param {HTMLElement} node The target node
15945          * @param {Roo.EventObject} e The raw event object
15946          */
15947             "dblclick" : true,
15948         /**
15949          * @event contextmenu
15950          * Fires when a template node is right clicked.
15951          * @param {Roo.View} this
15952          * @param {Number} index The index of the target node
15953          * @param {HTMLElement} node The target node
15954          * @param {Roo.EventObject} e The raw event object
15955          */
15956             "contextmenu" : true,
15957         /**
15958          * @event selectionchange
15959          * Fires when the selected nodes change.
15960          * @param {Roo.View} this
15961          * @param {Array} selections Array of the selected nodes
15962          */
15963             "selectionchange" : true,
15964     
15965         /**
15966          * @event beforeselect
15967          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15968          * @param {Roo.View} this
15969          * @param {HTMLElement} node The node to be selected
15970          * @param {Array} selections Array of currently selected nodes
15971          */
15972             "beforeselect" : true,
15973         /**
15974          * @event preparedata
15975          * Fires on every row to render, to allow you to change the data.
15976          * @param {Roo.View} this
15977          * @param {Object} data to be rendered (change this)
15978          */
15979           "preparedata" : true
15980           
15981           
15982         });
15983
15984
15985
15986     this.el.on({
15987         "click": this.onClick,
15988         "dblclick": this.onDblClick,
15989         "contextmenu": this.onContextMenu,
15990         scope:this
15991     });
15992
15993     this.selections = [];
15994     this.nodes = [];
15995     this.cmp = new Roo.CompositeElementLite([]);
15996     if(this.store){
15997         this.store = Roo.factory(this.store, Roo.data);
15998         this.setStore(this.store, true);
15999     }
16000     
16001     if ( this.footer && this.footer.xtype) {
16002            
16003          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16004         
16005         this.footer.dataSource = this.store;
16006         this.footer.container = fctr;
16007         this.footer = Roo.factory(this.footer, Roo);
16008         fctr.insertFirst(this.el);
16009         
16010         // this is a bit insane - as the paging toolbar seems to detach the el..
16011 //        dom.parentNode.parentNode.parentNode
16012          // they get detached?
16013     }
16014     
16015     
16016     Roo.View.superclass.constructor.call(this);
16017     
16018     
16019 };
16020
16021 Roo.extend(Roo.View, Roo.util.Observable, {
16022     
16023      /**
16024      * @cfg {Roo.data.Store} store Data store to load data from.
16025      */
16026     store : false,
16027     
16028     /**
16029      * @cfg {String|Roo.Element} el The container element.
16030      */
16031     el : '',
16032     
16033     /**
16034      * @cfg {String|Roo.Template} tpl The template used by this View 
16035      */
16036     tpl : false,
16037     /**
16038      * @cfg {String} dataName the named area of the template to use as the data area
16039      *                          Works with domtemplates roo-name="name"
16040      */
16041     dataName: false,
16042     /**
16043      * @cfg {String} selectedClass The css class to add to selected nodes
16044      */
16045     selectedClass : "x-view-selected",
16046      /**
16047      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16048      */
16049     emptyText : "",
16050     
16051     /**
16052      * @cfg {String} text to display on mask (default Loading)
16053      */
16054     mask : false,
16055     /**
16056      * @cfg {Boolean} multiSelect Allow multiple selection
16057      */
16058     multiSelect : false,
16059     /**
16060      * @cfg {Boolean} singleSelect Allow single selection
16061      */
16062     singleSelect:  false,
16063     
16064     /**
16065      * @cfg {Boolean} toggleSelect - selecting 
16066      */
16067     toggleSelect : false,
16068     
16069     /**
16070      * @cfg {Boolean} tickable - selecting 
16071      */
16072     tickable : false,
16073     
16074     /**
16075      * Returns the element this view is bound to.
16076      * @return {Roo.Element}
16077      */
16078     getEl : function(){
16079         return this.wrapEl;
16080     },
16081     
16082     
16083
16084     /**
16085      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16086      */
16087     refresh : function(){
16088         //Roo.log('refresh');
16089         var t = this.tpl;
16090         
16091         // if we are using something like 'domtemplate', then
16092         // the what gets used is:
16093         // t.applySubtemplate(NAME, data, wrapping data..)
16094         // the outer template then get' applied with
16095         //     the store 'extra data'
16096         // and the body get's added to the
16097         //      roo-name="data" node?
16098         //      <span class='roo-tpl-{name}'></span> ?????
16099         
16100         
16101         
16102         this.clearSelections();
16103         this.el.update("");
16104         var html = [];
16105         var records = this.store.getRange();
16106         if(records.length < 1) {
16107             
16108             // is this valid??  = should it render a template??
16109             
16110             this.el.update(this.emptyText);
16111             return;
16112         }
16113         var el = this.el;
16114         if (this.dataName) {
16115             this.el.update(t.apply(this.store.meta)); //????
16116             el = this.el.child('.roo-tpl-' + this.dataName);
16117         }
16118         
16119         for(var i = 0, len = records.length; i < len; i++){
16120             var data = this.prepareData(records[i].data, i, records[i]);
16121             this.fireEvent("preparedata", this, data, i, records[i]);
16122             
16123             var d = Roo.apply({}, data);
16124             
16125             if(this.tickable){
16126                 Roo.apply(d, {'roo-id' : Roo.id()});
16127                 
16128                 var _this = this;
16129             
16130                 Roo.each(this.parent.item, function(item){
16131                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16132                         return;
16133                     }
16134                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16135                 });
16136             }
16137             
16138             html[html.length] = Roo.util.Format.trim(
16139                 this.dataName ?
16140                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16141                     t.apply(d)
16142             );
16143         }
16144         
16145         
16146         
16147         el.update(html.join(""));
16148         this.nodes = el.dom.childNodes;
16149         this.updateIndexes(0);
16150     },
16151     
16152
16153     /**
16154      * Function to override to reformat the data that is sent to
16155      * the template for each node.
16156      * DEPRICATED - use the preparedata event handler.
16157      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16158      * a JSON object for an UpdateManager bound view).
16159      */
16160     prepareData : function(data, index, record)
16161     {
16162         this.fireEvent("preparedata", this, data, index, record);
16163         return data;
16164     },
16165
16166     onUpdate : function(ds, record){
16167         // Roo.log('on update');   
16168         this.clearSelections();
16169         var index = this.store.indexOf(record);
16170         var n = this.nodes[index];
16171         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16172         n.parentNode.removeChild(n);
16173         this.updateIndexes(index, index);
16174     },
16175
16176     
16177     
16178 // --------- FIXME     
16179     onAdd : function(ds, records, index)
16180     {
16181         //Roo.log(['on Add', ds, records, index] );        
16182         this.clearSelections();
16183         if(this.nodes.length == 0){
16184             this.refresh();
16185             return;
16186         }
16187         var n = this.nodes[index];
16188         for(var i = 0, len = records.length; i < len; i++){
16189             var d = this.prepareData(records[i].data, i, records[i]);
16190             if(n){
16191                 this.tpl.insertBefore(n, d);
16192             }else{
16193                 
16194                 this.tpl.append(this.el, d);
16195             }
16196         }
16197         this.updateIndexes(index);
16198     },
16199
16200     onRemove : function(ds, record, index){
16201        // Roo.log('onRemove');
16202         this.clearSelections();
16203         var el = this.dataName  ?
16204             this.el.child('.roo-tpl-' + this.dataName) :
16205             this.el; 
16206         
16207         el.dom.removeChild(this.nodes[index]);
16208         this.updateIndexes(index);
16209     },
16210
16211     /**
16212      * Refresh an individual node.
16213      * @param {Number} index
16214      */
16215     refreshNode : function(index){
16216         this.onUpdate(this.store, this.store.getAt(index));
16217     },
16218
16219     updateIndexes : function(startIndex, endIndex){
16220         var ns = this.nodes;
16221         startIndex = startIndex || 0;
16222         endIndex = endIndex || ns.length - 1;
16223         for(var i = startIndex; i <= endIndex; i++){
16224             ns[i].nodeIndex = i;
16225         }
16226     },
16227
16228     /**
16229      * Changes the data store this view uses and refresh the view.
16230      * @param {Store} store
16231      */
16232     setStore : function(store, initial){
16233         if(!initial && this.store){
16234             this.store.un("datachanged", this.refresh);
16235             this.store.un("add", this.onAdd);
16236             this.store.un("remove", this.onRemove);
16237             this.store.un("update", this.onUpdate);
16238             this.store.un("clear", this.refresh);
16239             this.store.un("beforeload", this.onBeforeLoad);
16240             this.store.un("load", this.onLoad);
16241             this.store.un("loadexception", this.onLoad);
16242         }
16243         if(store){
16244           
16245             store.on("datachanged", this.refresh, this);
16246             store.on("add", this.onAdd, this);
16247             store.on("remove", this.onRemove, this);
16248             store.on("update", this.onUpdate, this);
16249             store.on("clear", this.refresh, this);
16250             store.on("beforeload", this.onBeforeLoad, this);
16251             store.on("load", this.onLoad, this);
16252             store.on("loadexception", this.onLoad, this);
16253         }
16254         
16255         if(store){
16256             this.refresh();
16257         }
16258     },
16259     /**
16260      * onbeforeLoad - masks the loading area.
16261      *
16262      */
16263     onBeforeLoad : function(store,opts)
16264     {
16265          //Roo.log('onBeforeLoad');   
16266         if (!opts.add) {
16267             this.el.update("");
16268         }
16269         this.el.mask(this.mask ? this.mask : "Loading" ); 
16270     },
16271     onLoad : function ()
16272     {
16273         this.el.unmask();
16274     },
16275     
16276
16277     /**
16278      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16279      * @param {HTMLElement} node
16280      * @return {HTMLElement} The template node
16281      */
16282     findItemFromChild : function(node){
16283         var el = this.dataName  ?
16284             this.el.child('.roo-tpl-' + this.dataName,true) :
16285             this.el.dom; 
16286         
16287         if(!node || node.parentNode == el){
16288                     return node;
16289             }
16290             var p = node.parentNode;
16291             while(p && p != el){
16292             if(p.parentNode == el){
16293                 return p;
16294             }
16295             p = p.parentNode;
16296         }
16297             return null;
16298     },
16299
16300     /** @ignore */
16301     onClick : function(e){
16302         var item = this.findItemFromChild(e.getTarget());
16303         if(item){
16304             var index = this.indexOf(item);
16305             if(this.onItemClick(item, index, e) !== false){
16306                 this.fireEvent("click", this, index, item, e);
16307             }
16308         }else{
16309             this.clearSelections();
16310         }
16311     },
16312
16313     /** @ignore */
16314     onContextMenu : function(e){
16315         var item = this.findItemFromChild(e.getTarget());
16316         if(item){
16317             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16318         }
16319     },
16320
16321     /** @ignore */
16322     onDblClick : function(e){
16323         var item = this.findItemFromChild(e.getTarget());
16324         if(item){
16325             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16326         }
16327     },
16328
16329     onItemClick : function(item, index, e)
16330     {
16331         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16332             return false;
16333         }
16334         if (this.toggleSelect) {
16335             var m = this.isSelected(item) ? 'unselect' : 'select';
16336             //Roo.log(m);
16337             var _t = this;
16338             _t[m](item, true, false);
16339             return true;
16340         }
16341         if(this.multiSelect || this.singleSelect){
16342             if(this.multiSelect && e.shiftKey && this.lastSelection){
16343                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16344             }else{
16345                 this.select(item, this.multiSelect && e.ctrlKey);
16346                 this.lastSelection = item;
16347             }
16348             
16349             if(!this.tickable){
16350                 e.preventDefault();
16351             }
16352             
16353         }
16354         return true;
16355     },
16356
16357     /**
16358      * Get the number of selected nodes.
16359      * @return {Number}
16360      */
16361     getSelectionCount : function(){
16362         return this.selections.length;
16363     },
16364
16365     /**
16366      * Get the currently selected nodes.
16367      * @return {Array} An array of HTMLElements
16368      */
16369     getSelectedNodes : function(){
16370         return this.selections;
16371     },
16372
16373     /**
16374      * Get the indexes of the selected nodes.
16375      * @return {Array}
16376      */
16377     getSelectedIndexes : function(){
16378         var indexes = [], s = this.selections;
16379         for(var i = 0, len = s.length; i < len; i++){
16380             indexes.push(s[i].nodeIndex);
16381         }
16382         return indexes;
16383     },
16384
16385     /**
16386      * Clear all selections
16387      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16388      */
16389     clearSelections : function(suppressEvent){
16390         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16391             this.cmp.elements = this.selections;
16392             this.cmp.removeClass(this.selectedClass);
16393             this.selections = [];
16394             if(!suppressEvent){
16395                 this.fireEvent("selectionchange", this, this.selections);
16396             }
16397         }
16398     },
16399
16400     /**
16401      * Returns true if the passed node is selected
16402      * @param {HTMLElement/Number} node The node or node index
16403      * @return {Boolean}
16404      */
16405     isSelected : function(node){
16406         var s = this.selections;
16407         if(s.length < 1){
16408             return false;
16409         }
16410         node = this.getNode(node);
16411         return s.indexOf(node) !== -1;
16412     },
16413
16414     /**
16415      * Selects nodes.
16416      * @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
16417      * @param {Boolean} keepExisting (optional) true to keep existing selections
16418      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16419      */
16420     select : function(nodeInfo, keepExisting, suppressEvent){
16421         if(nodeInfo instanceof Array){
16422             if(!keepExisting){
16423                 this.clearSelections(true);
16424             }
16425             for(var i = 0, len = nodeInfo.length; i < len; i++){
16426                 this.select(nodeInfo[i], true, true);
16427             }
16428             return;
16429         } 
16430         var node = this.getNode(nodeInfo);
16431         if(!node || this.isSelected(node)){
16432             return; // already selected.
16433         }
16434         if(!keepExisting){
16435             this.clearSelections(true);
16436         }
16437         
16438         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16439             Roo.fly(node).addClass(this.selectedClass);
16440             this.selections.push(node);
16441             if(!suppressEvent){
16442                 this.fireEvent("selectionchange", this, this.selections);
16443             }
16444         }
16445         
16446         
16447     },
16448       /**
16449      * Unselects nodes.
16450      * @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
16451      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16452      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16453      */
16454     unselect : function(nodeInfo, keepExisting, suppressEvent)
16455     {
16456         if(nodeInfo instanceof Array){
16457             Roo.each(this.selections, function(s) {
16458                 this.unselect(s, nodeInfo);
16459             }, this);
16460             return;
16461         }
16462         var node = this.getNode(nodeInfo);
16463         if(!node || !this.isSelected(node)){
16464             //Roo.log("not selected");
16465             return; // not selected.
16466         }
16467         // fireevent???
16468         var ns = [];
16469         Roo.each(this.selections, function(s) {
16470             if (s == node ) {
16471                 Roo.fly(node).removeClass(this.selectedClass);
16472
16473                 return;
16474             }
16475             ns.push(s);
16476         },this);
16477         
16478         this.selections= ns;
16479         this.fireEvent("selectionchange", this, this.selections);
16480     },
16481
16482     /**
16483      * Gets a template node.
16484      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16485      * @return {HTMLElement} The node or null if it wasn't found
16486      */
16487     getNode : function(nodeInfo){
16488         if(typeof nodeInfo == "string"){
16489             return document.getElementById(nodeInfo);
16490         }else if(typeof nodeInfo == "number"){
16491             return this.nodes[nodeInfo];
16492         }
16493         return nodeInfo;
16494     },
16495
16496     /**
16497      * Gets a range template nodes.
16498      * @param {Number} startIndex
16499      * @param {Number} endIndex
16500      * @return {Array} An array of nodes
16501      */
16502     getNodes : function(start, end){
16503         var ns = this.nodes;
16504         start = start || 0;
16505         end = typeof end == "undefined" ? ns.length - 1 : end;
16506         var nodes = [];
16507         if(start <= end){
16508             for(var i = start; i <= end; i++){
16509                 nodes.push(ns[i]);
16510             }
16511         } else{
16512             for(var i = start; i >= end; i--){
16513                 nodes.push(ns[i]);
16514             }
16515         }
16516         return nodes;
16517     },
16518
16519     /**
16520      * Finds the index of the passed node
16521      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16522      * @return {Number} The index of the node or -1
16523      */
16524     indexOf : function(node){
16525         node = this.getNode(node);
16526         if(typeof node.nodeIndex == "number"){
16527             return node.nodeIndex;
16528         }
16529         var ns = this.nodes;
16530         for(var i = 0, len = ns.length; i < len; i++){
16531             if(ns[i] == node){
16532                 return i;
16533             }
16534         }
16535         return -1;
16536     }
16537 });
16538 /*
16539  * - LGPL
16540  *
16541  * based on jquery fullcalendar
16542  * 
16543  */
16544
16545 Roo.bootstrap = Roo.bootstrap || {};
16546 /**
16547  * @class Roo.bootstrap.Calendar
16548  * @extends Roo.bootstrap.Component
16549  * Bootstrap Calendar class
16550  * @cfg {Boolean} loadMask (true|false) default false
16551  * @cfg {Object} header generate the user specific header of the calendar, default false
16552
16553  * @constructor
16554  * Create a new Container
16555  * @param {Object} config The config object
16556  */
16557
16558
16559
16560 Roo.bootstrap.Calendar = function(config){
16561     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16562      this.addEvents({
16563         /**
16564              * @event select
16565              * Fires when a date is selected
16566              * @param {DatePicker} this
16567              * @param {Date} date The selected date
16568              */
16569         'select': true,
16570         /**
16571              * @event monthchange
16572              * Fires when the displayed month changes 
16573              * @param {DatePicker} this
16574              * @param {Date} date The selected month
16575              */
16576         'monthchange': true,
16577         /**
16578              * @event evententer
16579              * Fires when mouse over an event
16580              * @param {Calendar} this
16581              * @param {event} Event
16582              */
16583         'evententer': true,
16584         /**
16585              * @event eventleave
16586              * Fires when the mouse leaves an
16587              * @param {Calendar} this
16588              * @param {event}
16589              */
16590         'eventleave': true,
16591         /**
16592              * @event eventclick
16593              * Fires when the mouse click an
16594              * @param {Calendar} this
16595              * @param {event}
16596              */
16597         'eventclick': true
16598         
16599     });
16600
16601 };
16602
16603 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16604     
16605      /**
16606      * @cfg {Number} startDay
16607      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16608      */
16609     startDay : 0,
16610     
16611     loadMask : false,
16612     
16613     header : false,
16614       
16615     getAutoCreate : function(){
16616         
16617         
16618         var fc_button = function(name, corner, style, content ) {
16619             return Roo.apply({},{
16620                 tag : 'span',
16621                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16622                          (corner.length ?
16623                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16624                             ''
16625                         ),
16626                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16627                 unselectable: 'on'
16628             });
16629         };
16630         
16631         var header = {};
16632         
16633         if(!this.header){
16634             header = {
16635                 tag : 'table',
16636                 cls : 'fc-header',
16637                 style : 'width:100%',
16638                 cn : [
16639                     {
16640                         tag: 'tr',
16641                         cn : [
16642                             {
16643                                 tag : 'td',
16644                                 cls : 'fc-header-left',
16645                                 cn : [
16646                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16647                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16648                                     { tag: 'span', cls: 'fc-header-space' },
16649                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16650
16651
16652                                 ]
16653                             },
16654
16655                             {
16656                                 tag : 'td',
16657                                 cls : 'fc-header-center',
16658                                 cn : [
16659                                     {
16660                                         tag: 'span',
16661                                         cls: 'fc-header-title',
16662                                         cn : {
16663                                             tag: 'H2',
16664                                             html : 'month / year'
16665                                         }
16666                                     }
16667
16668                                 ]
16669                             },
16670                             {
16671                                 tag : 'td',
16672                                 cls : 'fc-header-right',
16673                                 cn : [
16674                               /*      fc_button('month', 'left', '', 'month' ),
16675                                     fc_button('week', '', '', 'week' ),
16676                                     fc_button('day', 'right', '', 'day' )
16677                                 */    
16678
16679                                 ]
16680                             }
16681
16682                         ]
16683                     }
16684                 ]
16685             };
16686         }
16687         
16688         header = this.header;
16689         
16690        
16691         var cal_heads = function() {
16692             var ret = [];
16693             // fixme - handle this.
16694             
16695             for (var i =0; i < Date.dayNames.length; i++) {
16696                 var d = Date.dayNames[i];
16697                 ret.push({
16698                     tag: 'th',
16699                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16700                     html : d.substring(0,3)
16701                 });
16702                 
16703             }
16704             ret[0].cls += ' fc-first';
16705             ret[6].cls += ' fc-last';
16706             return ret;
16707         };
16708         var cal_cell = function(n) {
16709             return  {
16710                 tag: 'td',
16711                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16712                 cn : [
16713                     {
16714                         cn : [
16715                             {
16716                                 cls: 'fc-day-number',
16717                                 html: 'D'
16718                             },
16719                             {
16720                                 cls: 'fc-day-content',
16721                              
16722                                 cn : [
16723                                      {
16724                                         style: 'position: relative;' // height: 17px;
16725                                     }
16726                                 ]
16727                             }
16728                             
16729                             
16730                         ]
16731                     }
16732                 ]
16733                 
16734             }
16735         };
16736         var cal_rows = function() {
16737             
16738             var ret = [];
16739             for (var r = 0; r < 6; r++) {
16740                 var row= {
16741                     tag : 'tr',
16742                     cls : 'fc-week',
16743                     cn : []
16744                 };
16745                 
16746                 for (var i =0; i < Date.dayNames.length; i++) {
16747                     var d = Date.dayNames[i];
16748                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16749
16750                 }
16751                 row.cn[0].cls+=' fc-first';
16752                 row.cn[0].cn[0].style = 'min-height:90px';
16753                 row.cn[6].cls+=' fc-last';
16754                 ret.push(row);
16755                 
16756             }
16757             ret[0].cls += ' fc-first';
16758             ret[4].cls += ' fc-prev-last';
16759             ret[5].cls += ' fc-last';
16760             return ret;
16761             
16762         };
16763         
16764         var cal_table = {
16765             tag: 'table',
16766             cls: 'fc-border-separate',
16767             style : 'width:100%',
16768             cellspacing  : 0,
16769             cn : [
16770                 { 
16771                     tag: 'thead',
16772                     cn : [
16773                         { 
16774                             tag: 'tr',
16775                             cls : 'fc-first fc-last',
16776                             cn : cal_heads()
16777                         }
16778                     ]
16779                 },
16780                 { 
16781                     tag: 'tbody',
16782                     cn : cal_rows()
16783                 }
16784                   
16785             ]
16786         };
16787          
16788          var cfg = {
16789             cls : 'fc fc-ltr',
16790             cn : [
16791                 header,
16792                 {
16793                     cls : 'fc-content',
16794                     style : "position: relative;",
16795                     cn : [
16796                         {
16797                             cls : 'fc-view fc-view-month fc-grid',
16798                             style : 'position: relative',
16799                             unselectable : 'on',
16800                             cn : [
16801                                 {
16802                                     cls : 'fc-event-container',
16803                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16804                                 },
16805                                 cal_table
16806                             ]
16807                         }
16808                     ]
16809     
16810                 }
16811            ] 
16812             
16813         };
16814         
16815          
16816         
16817         return cfg;
16818     },
16819     
16820     
16821     initEvents : function()
16822     {
16823         if(!this.store){
16824             throw "can not find store for calendar";
16825         }
16826         
16827         var mark = {
16828             tag: "div",
16829             cls:"x-dlg-mask",
16830             style: "text-align:center",
16831             cn: [
16832                 {
16833                     tag: "div",
16834                     style: "background-color:white;width:50%;margin:250 auto",
16835                     cn: [
16836                         {
16837                             tag: "img",
16838                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16839                         },
16840                         {
16841                             tag: "span",
16842                             html: "Loading"
16843                         }
16844                         
16845                     ]
16846                 }
16847             ]
16848         };
16849         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16850         
16851         var size = this.el.select('.fc-content', true).first().getSize();
16852         this.maskEl.setSize(size.width, size.height);
16853         this.maskEl.enableDisplayMode("block");
16854         if(!this.loadMask){
16855             this.maskEl.hide();
16856         }
16857         
16858         this.store = Roo.factory(this.store, Roo.data);
16859         this.store.on('load', this.onLoad, this);
16860         this.store.on('beforeload', this.onBeforeLoad, this);
16861         
16862         this.resize();
16863         
16864         this.cells = this.el.select('.fc-day',true);
16865         //Roo.log(this.cells);
16866         this.textNodes = this.el.query('.fc-day-number');
16867         this.cells.addClassOnOver('fc-state-hover');
16868         
16869         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16870         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16871         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16872         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16873         
16874         this.on('monthchange', this.onMonthChange, this);
16875         
16876         this.update(new Date().clearTime());
16877     },
16878     
16879     resize : function() {
16880         var sz  = this.el.getSize();
16881         
16882         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16883         this.el.select('.fc-day-content div',true).setHeight(34);
16884     },
16885     
16886     
16887     // private
16888     showPrevMonth : function(e){
16889         this.update(this.activeDate.add("mo", -1));
16890     },
16891     showToday : function(e){
16892         this.update(new Date().clearTime());
16893     },
16894     // private
16895     showNextMonth : function(e){
16896         this.update(this.activeDate.add("mo", 1));
16897     },
16898
16899     // private
16900     showPrevYear : function(){
16901         this.update(this.activeDate.add("y", -1));
16902     },
16903
16904     // private
16905     showNextYear : function(){
16906         this.update(this.activeDate.add("y", 1));
16907     },
16908
16909     
16910    // private
16911     update : function(date)
16912     {
16913         var vd = this.activeDate;
16914         this.activeDate = date;
16915 //        if(vd && this.el){
16916 //            var t = date.getTime();
16917 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16918 //                Roo.log('using add remove');
16919 //                
16920 //                this.fireEvent('monthchange', this, date);
16921 //                
16922 //                this.cells.removeClass("fc-state-highlight");
16923 //                this.cells.each(function(c){
16924 //                   if(c.dateValue == t){
16925 //                       c.addClass("fc-state-highlight");
16926 //                       setTimeout(function(){
16927 //                            try{c.dom.firstChild.focus();}catch(e){}
16928 //                       }, 50);
16929 //                       return false;
16930 //                   }
16931 //                   return true;
16932 //                });
16933 //                return;
16934 //            }
16935 //        }
16936         
16937         var days = date.getDaysInMonth();
16938         
16939         var firstOfMonth = date.getFirstDateOfMonth();
16940         var startingPos = firstOfMonth.getDay()-this.startDay;
16941         
16942         if(startingPos < this.startDay){
16943             startingPos += 7;
16944         }
16945         
16946         var pm = date.add(Date.MONTH, -1);
16947         var prevStart = pm.getDaysInMonth()-startingPos;
16948 //        
16949         this.cells = this.el.select('.fc-day',true);
16950         this.textNodes = this.el.query('.fc-day-number');
16951         this.cells.addClassOnOver('fc-state-hover');
16952         
16953         var cells = this.cells.elements;
16954         var textEls = this.textNodes;
16955         
16956         Roo.each(cells, function(cell){
16957             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16958         });
16959         
16960         days += startingPos;
16961
16962         // convert everything to numbers so it's fast
16963         var day = 86400000;
16964         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16965         //Roo.log(d);
16966         //Roo.log(pm);
16967         //Roo.log(prevStart);
16968         
16969         var today = new Date().clearTime().getTime();
16970         var sel = date.clearTime().getTime();
16971         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16972         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16973         var ddMatch = this.disabledDatesRE;
16974         var ddText = this.disabledDatesText;
16975         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16976         var ddaysText = this.disabledDaysText;
16977         var format = this.format;
16978         
16979         var setCellClass = function(cal, cell){
16980             cell.row = 0;
16981             cell.events = [];
16982             cell.more = [];
16983             //Roo.log('set Cell Class');
16984             cell.title = "";
16985             var t = d.getTime();
16986             
16987             //Roo.log(d);
16988             
16989             cell.dateValue = t;
16990             if(t == today){
16991                 cell.className += " fc-today";
16992                 cell.className += " fc-state-highlight";
16993                 cell.title = cal.todayText;
16994             }
16995             if(t == sel){
16996                 // disable highlight in other month..
16997                 //cell.className += " fc-state-highlight";
16998                 
16999             }
17000             // disabling
17001             if(t < min) {
17002                 cell.className = " fc-state-disabled";
17003                 cell.title = cal.minText;
17004                 return;
17005             }
17006             if(t > max) {
17007                 cell.className = " fc-state-disabled";
17008                 cell.title = cal.maxText;
17009                 return;
17010             }
17011             if(ddays){
17012                 if(ddays.indexOf(d.getDay()) != -1){
17013                     cell.title = ddaysText;
17014                     cell.className = " fc-state-disabled";
17015                 }
17016             }
17017             if(ddMatch && format){
17018                 var fvalue = d.dateFormat(format);
17019                 if(ddMatch.test(fvalue)){
17020                     cell.title = ddText.replace("%0", fvalue);
17021                     cell.className = " fc-state-disabled";
17022                 }
17023             }
17024             
17025             if (!cell.initialClassName) {
17026                 cell.initialClassName = cell.dom.className;
17027             }
17028             
17029             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17030         };
17031
17032         var i = 0;
17033         
17034         for(; i < startingPos; i++) {
17035             textEls[i].innerHTML = (++prevStart);
17036             d.setDate(d.getDate()+1);
17037             
17038             cells[i].className = "fc-past fc-other-month";
17039             setCellClass(this, cells[i]);
17040         }
17041         
17042         var intDay = 0;
17043         
17044         for(; i < days; i++){
17045             intDay = i - startingPos + 1;
17046             textEls[i].innerHTML = (intDay);
17047             d.setDate(d.getDate()+1);
17048             
17049             cells[i].className = ''; // "x-date-active";
17050             setCellClass(this, cells[i]);
17051         }
17052         var extraDays = 0;
17053         
17054         for(; i < 42; i++) {
17055             textEls[i].innerHTML = (++extraDays);
17056             d.setDate(d.getDate()+1);
17057             
17058             cells[i].className = "fc-future fc-other-month";
17059             setCellClass(this, cells[i]);
17060         }
17061         
17062         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17063         
17064         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17065         
17066         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17067         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17068         
17069         if(totalRows != 6){
17070             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17071             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17072         }
17073         
17074         this.fireEvent('monthchange', this, date);
17075         
17076         
17077         /*
17078         if(!this.internalRender){
17079             var main = this.el.dom.firstChild;
17080             var w = main.offsetWidth;
17081             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17082             Roo.fly(main).setWidth(w);
17083             this.internalRender = true;
17084             // opera does not respect the auto grow header center column
17085             // then, after it gets a width opera refuses to recalculate
17086             // without a second pass
17087             if(Roo.isOpera && !this.secondPass){
17088                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17089                 this.secondPass = true;
17090                 this.update.defer(10, this, [date]);
17091             }
17092         }
17093         */
17094         
17095     },
17096     
17097     findCell : function(dt) {
17098         dt = dt.clearTime().getTime();
17099         var ret = false;
17100         this.cells.each(function(c){
17101             //Roo.log("check " +c.dateValue + '?=' + dt);
17102             if(c.dateValue == dt){
17103                 ret = c;
17104                 return false;
17105             }
17106             return true;
17107         });
17108         
17109         return ret;
17110     },
17111     
17112     findCells : function(ev) {
17113         var s = ev.start.clone().clearTime().getTime();
17114        // Roo.log(s);
17115         var e= ev.end.clone().clearTime().getTime();
17116        // Roo.log(e);
17117         var ret = [];
17118         this.cells.each(function(c){
17119              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17120             
17121             if(c.dateValue > e){
17122                 return ;
17123             }
17124             if(c.dateValue < s){
17125                 return ;
17126             }
17127             ret.push(c);
17128         });
17129         
17130         return ret;    
17131     },
17132     
17133 //    findBestRow: function(cells)
17134 //    {
17135 //        var ret = 0;
17136 //        
17137 //        for (var i =0 ; i < cells.length;i++) {
17138 //            ret  = Math.max(cells[i].rows || 0,ret);
17139 //        }
17140 //        return ret;
17141 //        
17142 //    },
17143     
17144     
17145     addItem : function(ev)
17146     {
17147         // look for vertical location slot in
17148         var cells = this.findCells(ev);
17149         
17150 //        ev.row = this.findBestRow(cells);
17151         
17152         // work out the location.
17153         
17154         var crow = false;
17155         var rows = [];
17156         for(var i =0; i < cells.length; i++) {
17157             
17158             cells[i].row = cells[0].row;
17159             
17160             if(i == 0){
17161                 cells[i].row = cells[i].row + 1;
17162             }
17163             
17164             if (!crow) {
17165                 crow = {
17166                     start : cells[i],
17167                     end :  cells[i]
17168                 };
17169                 continue;
17170             }
17171             if (crow.start.getY() == cells[i].getY()) {
17172                 // on same row.
17173                 crow.end = cells[i];
17174                 continue;
17175             }
17176             // different row.
17177             rows.push(crow);
17178             crow = {
17179                 start: cells[i],
17180                 end : cells[i]
17181             };
17182             
17183         }
17184         
17185         rows.push(crow);
17186         ev.els = [];
17187         ev.rows = rows;
17188         ev.cells = cells;
17189         
17190         cells[0].events.push(ev);
17191         
17192         this.calevents.push(ev);
17193     },
17194     
17195     clearEvents: function() {
17196         
17197         if(!this.calevents){
17198             return;
17199         }
17200         
17201         Roo.each(this.cells.elements, function(c){
17202             c.row = 0;
17203             c.events = [];
17204             c.more = [];
17205         });
17206         
17207         Roo.each(this.calevents, function(e) {
17208             Roo.each(e.els, function(el) {
17209                 el.un('mouseenter' ,this.onEventEnter, this);
17210                 el.un('mouseleave' ,this.onEventLeave, this);
17211                 el.remove();
17212             },this);
17213         },this);
17214         
17215         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17216             e.remove();
17217         });
17218         
17219     },
17220     
17221     renderEvents: function()
17222     {   
17223         var _this = this;
17224         
17225         this.cells.each(function(c) {
17226             
17227             if(c.row < 5){
17228                 return;
17229             }
17230             
17231             var ev = c.events;
17232             
17233             var r = 4;
17234             if(c.row != c.events.length){
17235                 r = 4 - (4 - (c.row - c.events.length));
17236             }
17237             
17238             c.events = ev.slice(0, r);
17239             c.more = ev.slice(r);
17240             
17241             if(c.more.length && c.more.length == 1){
17242                 c.events.push(c.more.pop());
17243             }
17244             
17245             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17246             
17247         });
17248             
17249         this.cells.each(function(c) {
17250             
17251             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17252             
17253             
17254             for (var e = 0; e < c.events.length; e++){
17255                 var ev = c.events[e];
17256                 var rows = ev.rows;
17257                 
17258                 for(var i = 0; i < rows.length; i++) {
17259                 
17260                     // how many rows should it span..
17261
17262                     var  cfg = {
17263                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17264                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17265
17266                         unselectable : "on",
17267                         cn : [
17268                             {
17269                                 cls: 'fc-event-inner',
17270                                 cn : [
17271     //                                {
17272     //                                  tag:'span',
17273     //                                  cls: 'fc-event-time',
17274     //                                  html : cells.length > 1 ? '' : ev.time
17275     //                                },
17276                                     {
17277                                       tag:'span',
17278                                       cls: 'fc-event-title',
17279                                       html : String.format('{0}', ev.title)
17280                                     }
17281
17282
17283                                 ]
17284                             },
17285                             {
17286                                 cls: 'ui-resizable-handle ui-resizable-e',
17287                                 html : '&nbsp;&nbsp;&nbsp'
17288                             }
17289
17290                         ]
17291                     };
17292
17293                     if (i == 0) {
17294                         cfg.cls += ' fc-event-start';
17295                     }
17296                     if ((i+1) == rows.length) {
17297                         cfg.cls += ' fc-event-end';
17298                     }
17299
17300                     var ctr = _this.el.select('.fc-event-container',true).first();
17301                     var cg = ctr.createChild(cfg);
17302
17303                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17304                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17305
17306                     var r = (c.more.length) ? 1 : 0;
17307                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17308                     cg.setWidth(ebox.right - sbox.x -2);
17309
17310                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17311                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17312                     cg.on('click', _this.onEventClick, _this, ev);
17313
17314                     ev.els.push(cg);
17315                     
17316                 }
17317                 
17318             }
17319             
17320             
17321             if(c.more.length){
17322                 var  cfg = {
17323                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17324                     style : 'position: absolute',
17325                     unselectable : "on",
17326                     cn : [
17327                         {
17328                             cls: 'fc-event-inner',
17329                             cn : [
17330                                 {
17331                                   tag:'span',
17332                                   cls: 'fc-event-title',
17333                                   html : 'More'
17334                                 }
17335
17336
17337                             ]
17338                         },
17339                         {
17340                             cls: 'ui-resizable-handle ui-resizable-e',
17341                             html : '&nbsp;&nbsp;&nbsp'
17342                         }
17343
17344                     ]
17345                 };
17346
17347                 var ctr = _this.el.select('.fc-event-container',true).first();
17348                 var cg = ctr.createChild(cfg);
17349
17350                 var sbox = c.select('.fc-day-content',true).first().getBox();
17351                 var ebox = c.select('.fc-day-content',true).first().getBox();
17352                 //Roo.log(cg);
17353                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17354                 cg.setWidth(ebox.right - sbox.x -2);
17355
17356                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17357                 
17358             }
17359             
17360         });
17361         
17362         
17363         
17364     },
17365     
17366     onEventEnter: function (e, el,event,d) {
17367         this.fireEvent('evententer', this, el, event);
17368     },
17369     
17370     onEventLeave: function (e, el,event,d) {
17371         this.fireEvent('eventleave', this, el, event);
17372     },
17373     
17374     onEventClick: function (e, el,event,d) {
17375         this.fireEvent('eventclick', this, el, event);
17376     },
17377     
17378     onMonthChange: function () {
17379         this.store.load();
17380     },
17381     
17382     onMoreEventClick: function(e, el, more)
17383     {
17384         var _this = this;
17385         
17386         this.calpopover.placement = 'right';
17387         this.calpopover.setTitle('More');
17388         
17389         this.calpopover.setContent('');
17390         
17391         var ctr = this.calpopover.el.select('.popover-content', true).first();
17392         
17393         Roo.each(more, function(m){
17394             var cfg = {
17395                 cls : 'fc-event-hori fc-event-draggable',
17396                 html : m.title
17397             };
17398             var cg = ctr.createChild(cfg);
17399             
17400             cg.on('click', _this.onEventClick, _this, m);
17401         });
17402         
17403         this.calpopover.show(el);
17404         
17405         
17406     },
17407     
17408     onLoad: function () 
17409     {   
17410         this.calevents = [];
17411         var cal = this;
17412         
17413         if(this.store.getCount() > 0){
17414             this.store.data.each(function(d){
17415                cal.addItem({
17416                     id : d.data.id,
17417                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17418                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17419                     time : d.data.start_time,
17420                     title : d.data.title,
17421                     description : d.data.description,
17422                     venue : d.data.venue
17423                 });
17424             });
17425         }
17426         
17427         this.renderEvents();
17428         
17429         if(this.calevents.length && this.loadMask){
17430             this.maskEl.hide();
17431         }
17432     },
17433     
17434     onBeforeLoad: function()
17435     {
17436         this.clearEvents();
17437         if(this.loadMask){
17438             this.maskEl.show();
17439         }
17440     }
17441 });
17442
17443  
17444  /*
17445  * - LGPL
17446  *
17447  * element
17448  * 
17449  */
17450
17451 /**
17452  * @class Roo.bootstrap.Popover
17453  * @extends Roo.bootstrap.Component
17454  * Bootstrap Popover class
17455  * @cfg {String} html contents of the popover   (or false to use children..)
17456  * @cfg {String} title of popover (or false to hide)
17457  * @cfg {String} placement how it is placed
17458  * @cfg {String} trigger click || hover (or false to trigger manually)
17459  * @cfg {String} over what (parent or false to trigger manually.)
17460  * @cfg {Number} delay - delay before showing
17461  
17462  * @constructor
17463  * Create a new Popover
17464  * @param {Object} config The config object
17465  */
17466
17467 Roo.bootstrap.Popover = function(config){
17468     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17469     
17470     this.addEvents({
17471         // raw events
17472          /**
17473          * @event show
17474          * After the popover show
17475          * 
17476          * @param {Roo.bootstrap.Popover} this
17477          */
17478         "show" : true,
17479         /**
17480          * @event hide
17481          * After the popover hide
17482          * 
17483          * @param {Roo.bootstrap.Popover} this
17484          */
17485         "hide" : true
17486     });
17487 };
17488
17489 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17490     
17491     title: 'Fill in a title',
17492     html: false,
17493     
17494     placement : 'right',
17495     trigger : 'hover', // hover
17496     
17497     delay : 0,
17498     
17499     over: 'parent',
17500     
17501     can_build_overlaid : false,
17502     
17503     getChildContainer : function()
17504     {
17505         return this.el.select('.popover-content',true).first();
17506     },
17507     
17508     getAutoCreate : function(){
17509          
17510         var cfg = {
17511            cls : 'popover roo-dynamic',
17512            style: 'display:block',
17513            cn : [
17514                 {
17515                     cls : 'arrow'
17516                 },
17517                 {
17518                     cls : 'popover-inner',
17519                     cn : [
17520                         {
17521                             tag: 'h3',
17522                             cls: 'popover-title',
17523                             html : this.title
17524                         },
17525                         {
17526                             cls : 'popover-content',
17527                             html : this.html
17528                         }
17529                     ]
17530                     
17531                 }
17532            ]
17533         };
17534         
17535         return cfg;
17536     },
17537     setTitle: function(str)
17538     {
17539         this.title = str;
17540         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17541     },
17542     setContent: function(str)
17543     {
17544         this.html = str;
17545         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17546     },
17547     // as it get's added to the bottom of the page.
17548     onRender : function(ct, position)
17549     {
17550         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17551         if(!this.el){
17552             var cfg = Roo.apply({},  this.getAutoCreate());
17553             cfg.id = Roo.id();
17554             
17555             if (this.cls) {
17556                 cfg.cls += ' ' + this.cls;
17557             }
17558             if (this.style) {
17559                 cfg.style = this.style;
17560             }
17561             //Roo.log("adding to ");
17562             this.el = Roo.get(document.body).createChild(cfg, position);
17563 //            Roo.log(this.el);
17564         }
17565         this.initEvents();
17566     },
17567     
17568     initEvents : function()
17569     {
17570         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17571         this.el.enableDisplayMode('block');
17572         this.el.hide();
17573         if (this.over === false) {
17574             return; 
17575         }
17576         if (this.triggers === false) {
17577             return;
17578         }
17579         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17580         var triggers = this.trigger ? this.trigger.split(' ') : [];
17581         Roo.each(triggers, function(trigger) {
17582         
17583             if (trigger == 'click') {
17584                 on_el.on('click', this.toggle, this);
17585             } else if (trigger != 'manual') {
17586                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17587                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17588       
17589                 on_el.on(eventIn  ,this.enter, this);
17590                 on_el.on(eventOut, this.leave, this);
17591             }
17592         }, this);
17593         
17594     },
17595     
17596     
17597     // private
17598     timeout : null,
17599     hoverState : null,
17600     
17601     toggle : function () {
17602         this.hoverState == 'in' ? this.leave() : this.enter();
17603     },
17604     
17605     enter : function () {
17606         
17607         clearTimeout(this.timeout);
17608     
17609         this.hoverState = 'in';
17610     
17611         if (!this.delay || !this.delay.show) {
17612             this.show();
17613             return;
17614         }
17615         var _t = this;
17616         this.timeout = setTimeout(function () {
17617             if (_t.hoverState == 'in') {
17618                 _t.show();
17619             }
17620         }, this.delay.show)
17621     },
17622     
17623     leave : function() {
17624         clearTimeout(this.timeout);
17625     
17626         this.hoverState = 'out';
17627     
17628         if (!this.delay || !this.delay.hide) {
17629             this.hide();
17630             return;
17631         }
17632         var _t = this;
17633         this.timeout = setTimeout(function () {
17634             if (_t.hoverState == 'out') {
17635                 _t.hide();
17636             }
17637         }, this.delay.hide)
17638     },
17639     
17640     show : function (on_el)
17641     {
17642         if (!on_el) {
17643             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17644         }
17645         
17646         // set content.
17647         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17648         if (this.html !== false) {
17649             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17650         }
17651         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17652         if (!this.title.length) {
17653             this.el.select('.popover-title',true).hide();
17654         }
17655         
17656         var placement = typeof this.placement == 'function' ?
17657             this.placement.call(this, this.el, on_el) :
17658             this.placement;
17659             
17660         var autoToken = /\s?auto?\s?/i;
17661         var autoPlace = autoToken.test(placement);
17662         if (autoPlace) {
17663             placement = placement.replace(autoToken, '') || 'top';
17664         }
17665         
17666         //this.el.detach()
17667         //this.el.setXY([0,0]);
17668         this.el.show();
17669         this.el.dom.style.display='block';
17670         this.el.addClass(placement);
17671         
17672         //this.el.appendTo(on_el);
17673         
17674         var p = this.getPosition();
17675         var box = this.el.getBox();
17676         
17677         if (autoPlace) {
17678             // fixme..
17679         }
17680         var align = Roo.bootstrap.Popover.alignment[placement];
17681         
17682 //        Roo.log(align);
17683         this.el.alignTo(on_el, align[0],align[1]);
17684         //var arrow = this.el.select('.arrow',true).first();
17685         //arrow.set(align[2], 
17686         
17687         this.el.addClass('in');
17688         
17689         
17690         if (this.el.hasClass('fade')) {
17691             // fade it?
17692         }
17693         
17694         this.hoverState = 'in';
17695         
17696         this.fireEvent('show', this);
17697         
17698     },
17699     hide : function()
17700     {
17701         this.el.setXY([0,0]);
17702         this.el.removeClass('in');
17703         this.el.hide();
17704         this.hoverState = null;
17705         
17706         this.fireEvent('hide', this);
17707     }
17708     
17709 });
17710
17711 Roo.bootstrap.Popover.alignment = {
17712     'left' : ['r-l', [-10,0], 'right'],
17713     'right' : ['l-r', [10,0], 'left'],
17714     'bottom' : ['t-b', [0,10], 'top'],
17715     'top' : [ 'b-t', [0,-10], 'bottom']
17716 };
17717
17718  /*
17719  * - LGPL
17720  *
17721  * Progress
17722  * 
17723  */
17724
17725 /**
17726  * @class Roo.bootstrap.Progress
17727  * @extends Roo.bootstrap.Component
17728  * Bootstrap Progress class
17729  * @cfg {Boolean} striped striped of the progress bar
17730  * @cfg {Boolean} active animated of the progress bar
17731  * 
17732  * 
17733  * @constructor
17734  * Create a new Progress
17735  * @param {Object} config The config object
17736  */
17737
17738 Roo.bootstrap.Progress = function(config){
17739     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17740 };
17741
17742 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17743     
17744     striped : false,
17745     active: false,
17746     
17747     getAutoCreate : function(){
17748         var cfg = {
17749             tag: 'div',
17750             cls: 'progress'
17751         };
17752         
17753         
17754         if(this.striped){
17755             cfg.cls += ' progress-striped';
17756         }
17757       
17758         if(this.active){
17759             cfg.cls += ' active';
17760         }
17761         
17762         
17763         return cfg;
17764     }
17765    
17766 });
17767
17768  
17769
17770  /*
17771  * - LGPL
17772  *
17773  * ProgressBar
17774  * 
17775  */
17776
17777 /**
17778  * @class Roo.bootstrap.ProgressBar
17779  * @extends Roo.bootstrap.Component
17780  * Bootstrap ProgressBar class
17781  * @cfg {Number} aria_valuenow aria-value now
17782  * @cfg {Number} aria_valuemin aria-value min
17783  * @cfg {Number} aria_valuemax aria-value max
17784  * @cfg {String} label label for the progress bar
17785  * @cfg {String} panel (success | info | warning | danger )
17786  * @cfg {String} role role of the progress bar
17787  * @cfg {String} sr_only text
17788  * 
17789  * 
17790  * @constructor
17791  * Create a new ProgressBar
17792  * @param {Object} config The config object
17793  */
17794
17795 Roo.bootstrap.ProgressBar = function(config){
17796     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17797 };
17798
17799 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17800     
17801     aria_valuenow : 0,
17802     aria_valuemin : 0,
17803     aria_valuemax : 100,
17804     label : false,
17805     panel : false,
17806     role : false,
17807     sr_only: false,
17808     
17809     getAutoCreate : function()
17810     {
17811         
17812         var cfg = {
17813             tag: 'div',
17814             cls: 'progress-bar',
17815             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17816         };
17817         
17818         if(this.sr_only){
17819             cfg.cn = {
17820                 tag: 'span',
17821                 cls: 'sr-only',
17822                 html: this.sr_only
17823             }
17824         }
17825         
17826         if(this.role){
17827             cfg.role = this.role;
17828         }
17829         
17830         if(this.aria_valuenow){
17831             cfg['aria-valuenow'] = this.aria_valuenow;
17832         }
17833         
17834         if(this.aria_valuemin){
17835             cfg['aria-valuemin'] = this.aria_valuemin;
17836         }
17837         
17838         if(this.aria_valuemax){
17839             cfg['aria-valuemax'] = this.aria_valuemax;
17840         }
17841         
17842         if(this.label && !this.sr_only){
17843             cfg.html = this.label;
17844         }
17845         
17846         if(this.panel){
17847             cfg.cls += ' progress-bar-' + this.panel;
17848         }
17849         
17850         return cfg;
17851     },
17852     
17853     update : function(aria_valuenow)
17854     {
17855         this.aria_valuenow = aria_valuenow;
17856         
17857         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17858     }
17859    
17860 });
17861
17862  
17863
17864  /*
17865  * - LGPL
17866  *
17867  * column
17868  * 
17869  */
17870
17871 /**
17872  * @class Roo.bootstrap.TabGroup
17873  * @extends Roo.bootstrap.Column
17874  * Bootstrap Column class
17875  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17876  * @cfg {Boolean} carousel true to make the group behave like a carousel
17877  * @cfg {Boolean} bullets show bullets for the panels
17878  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17879  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17880  * @cfg {Boolean} showarrow (true|false) show arrow default true
17881  * 
17882  * @constructor
17883  * Create a new TabGroup
17884  * @param {Object} config The config object
17885  */
17886
17887 Roo.bootstrap.TabGroup = function(config){
17888     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17889     if (!this.navId) {
17890         this.navId = Roo.id();
17891     }
17892     this.tabs = [];
17893     Roo.bootstrap.TabGroup.register(this);
17894     
17895 };
17896
17897 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17898     
17899     carousel : false,
17900     transition : false,
17901     bullets : 0,
17902     timer : 0,
17903     autoslide : false,
17904     slideFn : false,
17905     slideOnTouch : false,
17906     showarrow : true,
17907     
17908     getAutoCreate : function()
17909     {
17910         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17911         
17912         cfg.cls += ' tab-content';
17913         
17914         if (this.carousel) {
17915             cfg.cls += ' carousel slide';
17916             
17917             cfg.cn = [{
17918                cls : 'carousel-inner',
17919                cn : []
17920             }];
17921         
17922             if(this.bullets  && !Roo.isTouch){
17923                 
17924                 var bullets = {
17925                     cls : 'carousel-bullets',
17926                     cn : []
17927                 };
17928                
17929                 if(this.bullets_cls){
17930                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17931                 }
17932                 
17933                 bullets.cn.push({
17934                     cls : 'clear'
17935                 });
17936                 
17937                 cfg.cn[0].cn.push(bullets);
17938             }
17939             
17940             if(this.showarrow){
17941                 cfg.cn[0].cn.push({
17942                     tag : 'div',
17943                     class : 'carousel-arrow',
17944                     cn : [
17945                         {
17946                             tag : 'div',
17947                             class : 'carousel-prev',
17948                             cn : [
17949                                 {
17950                                     tag : 'i',
17951                                     class : 'fa fa-chevron-left'
17952                                 }
17953                             ]
17954                         },
17955                         {
17956                             tag : 'div',
17957                             class : 'carousel-next',
17958                             cn : [
17959                                 {
17960                                     tag : 'i',
17961                                     class : 'fa fa-chevron-right'
17962                                 }
17963                             ]
17964                         }
17965                     ]
17966                 });
17967             }
17968             
17969         }
17970         
17971         return cfg;
17972     },
17973     
17974     initEvents:  function()
17975     {
17976 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17977 //            this.el.on("touchstart", this.onTouchStart, this);
17978 //        }
17979         
17980         if(this.autoslide){
17981             var _this = this;
17982             
17983             this.slideFn = window.setInterval(function() {
17984                 _this.showPanelNext();
17985             }, this.timer);
17986         }
17987         
17988         if(this.showarrow){
17989             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17990             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17991         }
17992         
17993         
17994     },
17995     
17996 //    onTouchStart : function(e, el, o)
17997 //    {
17998 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17999 //            return;
18000 //        }
18001 //        
18002 //        this.showPanelNext();
18003 //    },
18004     
18005     
18006     getChildContainer : function()
18007     {
18008         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18009     },
18010     
18011     /**
18012     * register a Navigation item
18013     * @param {Roo.bootstrap.NavItem} the navitem to add
18014     */
18015     register : function(item)
18016     {
18017         this.tabs.push( item);
18018         item.navId = this.navId; // not really needed..
18019         this.addBullet();
18020     
18021     },
18022     
18023     getActivePanel : function()
18024     {
18025         var r = false;
18026         Roo.each(this.tabs, function(t) {
18027             if (t.active) {
18028                 r = t;
18029                 return false;
18030             }
18031             return null;
18032         });
18033         return r;
18034         
18035     },
18036     getPanelByName : function(n)
18037     {
18038         var r = false;
18039         Roo.each(this.tabs, function(t) {
18040             if (t.tabId == n) {
18041                 r = t;
18042                 return false;
18043             }
18044             return null;
18045         });
18046         return r;
18047     },
18048     indexOfPanel : function(p)
18049     {
18050         var r = false;
18051         Roo.each(this.tabs, function(t,i) {
18052             if (t.tabId == p.tabId) {
18053                 r = i;
18054                 return false;
18055             }
18056             return null;
18057         });
18058         return r;
18059     },
18060     /**
18061      * show a specific panel
18062      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18063      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18064      */
18065     showPanel : function (pan)
18066     {
18067         if(this.transition || typeof(pan) == 'undefined'){
18068             Roo.log("waiting for the transitionend");
18069             return;
18070         }
18071         
18072         if (typeof(pan) == 'number') {
18073             pan = this.tabs[pan];
18074         }
18075         
18076         if (typeof(pan) == 'string') {
18077             pan = this.getPanelByName(pan);
18078         }
18079         
18080         var cur = this.getActivePanel();
18081         
18082         if(!pan || !cur){
18083             Roo.log('pan or acitve pan is undefined');
18084             return false;
18085         }
18086         
18087         if (pan.tabId == this.getActivePanel().tabId) {
18088             return true;
18089         }
18090         
18091         if (false === cur.fireEvent('beforedeactivate')) {
18092             return false;
18093         }
18094         
18095         if(this.bullets > 0 && !Roo.isTouch){
18096             this.setActiveBullet(this.indexOfPanel(pan));
18097         }
18098         
18099         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18100             
18101             this.transition = true;
18102             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18103             var lr = dir == 'next' ? 'left' : 'right';
18104             pan.el.addClass(dir); // or prev
18105             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18106             cur.el.addClass(lr); // or right
18107             pan.el.addClass(lr);
18108             
18109             var _this = this;
18110             cur.el.on('transitionend', function() {
18111                 Roo.log("trans end?");
18112                 
18113                 pan.el.removeClass([lr,dir]);
18114                 pan.setActive(true);
18115                 
18116                 cur.el.removeClass([lr]);
18117                 cur.setActive(false);
18118                 
18119                 _this.transition = false;
18120                 
18121             }, this, { single:  true } );
18122             
18123             return true;
18124         }
18125         
18126         cur.setActive(false);
18127         pan.setActive(true);
18128         
18129         return true;
18130         
18131     },
18132     showPanelNext : function()
18133     {
18134         var i = this.indexOfPanel(this.getActivePanel());
18135         
18136         if (i >= this.tabs.length - 1 && !this.autoslide) {
18137             return;
18138         }
18139         
18140         if (i >= this.tabs.length - 1 && this.autoslide) {
18141             i = -1;
18142         }
18143         
18144         this.showPanel(this.tabs[i+1]);
18145     },
18146     
18147     showPanelPrev : function()
18148     {
18149         var i = this.indexOfPanel(this.getActivePanel());
18150         
18151         if (i  < 1 && !this.autoslide) {
18152             return;
18153         }
18154         
18155         if (i < 1 && this.autoslide) {
18156             i = this.tabs.length;
18157         }
18158         
18159         this.showPanel(this.tabs[i-1]);
18160     },
18161     
18162     
18163     addBullet: function()
18164     {
18165         if(!this.bullets || Roo.isTouch){
18166             return;
18167         }
18168         var ctr = this.el.select('.carousel-bullets',true).first();
18169         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18170         var bullet = ctr.createChild({
18171             cls : 'bullet bullet-' + i
18172         },ctr.dom.lastChild);
18173         
18174         
18175         var _this = this;
18176         
18177         bullet.on('click', (function(e, el, o, ii, t){
18178
18179             e.preventDefault();
18180
18181             this.showPanel(ii);
18182
18183             if(this.autoslide && this.slideFn){
18184                 clearInterval(this.slideFn);
18185                 this.slideFn = window.setInterval(function() {
18186                     _this.showPanelNext();
18187                 }, this.timer);
18188             }
18189
18190         }).createDelegate(this, [i, bullet], true));
18191                 
18192         
18193     },
18194      
18195     setActiveBullet : function(i)
18196     {
18197         if(Roo.isTouch){
18198             return;
18199         }
18200         
18201         Roo.each(this.el.select('.bullet', true).elements, function(el){
18202             el.removeClass('selected');
18203         });
18204
18205         var bullet = this.el.select('.bullet-' + i, true).first();
18206         
18207         if(!bullet){
18208             return;
18209         }
18210         
18211         bullet.addClass('selected');
18212     }
18213     
18214     
18215   
18216 });
18217
18218  
18219
18220  
18221  
18222 Roo.apply(Roo.bootstrap.TabGroup, {
18223     
18224     groups: {},
18225      /**
18226     * register a Navigation Group
18227     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18228     */
18229     register : function(navgrp)
18230     {
18231         this.groups[navgrp.navId] = navgrp;
18232         
18233     },
18234     /**
18235     * fetch a Navigation Group based on the navigation ID
18236     * if one does not exist , it will get created.
18237     * @param {string} the navgroup to add
18238     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18239     */
18240     get: function(navId) {
18241         if (typeof(this.groups[navId]) == 'undefined') {
18242             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18243         }
18244         return this.groups[navId] ;
18245     }
18246     
18247     
18248     
18249 });
18250
18251  /*
18252  * - LGPL
18253  *
18254  * TabPanel
18255  * 
18256  */
18257
18258 /**
18259  * @class Roo.bootstrap.TabPanel
18260  * @extends Roo.bootstrap.Component
18261  * Bootstrap TabPanel class
18262  * @cfg {Boolean} active panel active
18263  * @cfg {String} html panel content
18264  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18265  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18266  * @cfg {String} href click to link..
18267  * 
18268  * 
18269  * @constructor
18270  * Create a new TabPanel
18271  * @param {Object} config The config object
18272  */
18273
18274 Roo.bootstrap.TabPanel = function(config){
18275     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18276     this.addEvents({
18277         /**
18278              * @event changed
18279              * Fires when the active status changes
18280              * @param {Roo.bootstrap.TabPanel} this
18281              * @param {Boolean} state the new state
18282             
18283          */
18284         'changed': true,
18285         /**
18286              * @event beforedeactivate
18287              * Fires before a tab is de-activated - can be used to do validation on a form.
18288              * @param {Roo.bootstrap.TabPanel} this
18289              * @return {Boolean} false if there is an error
18290             
18291          */
18292         'beforedeactivate': true
18293      });
18294     
18295     this.tabId = this.tabId || Roo.id();
18296   
18297 };
18298
18299 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18300     
18301     active: false,
18302     html: false,
18303     tabId: false,
18304     navId : false,
18305     href : '',
18306     
18307     getAutoCreate : function(){
18308         var cfg = {
18309             tag: 'div',
18310             // item is needed for carousel - not sure if it has any effect otherwise
18311             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18312             html: this.html || ''
18313         };
18314         
18315         if(this.active){
18316             cfg.cls += ' active';
18317         }
18318         
18319         if(this.tabId){
18320             cfg.tabId = this.tabId;
18321         }
18322         
18323         
18324         return cfg;
18325     },
18326     
18327     initEvents:  function()
18328     {
18329         var p = this.parent();
18330         
18331         this.navId = this.navId || p.navId;
18332         
18333         if (typeof(this.navId) != 'undefined') {
18334             // not really needed.. but just in case.. parent should be a NavGroup.
18335             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18336             
18337             tg.register(this);
18338             
18339             var i = tg.tabs.length - 1;
18340             
18341             if(this.active && tg.bullets > 0 && i < tg.bullets){
18342                 tg.setActiveBullet(i);
18343             }
18344         }
18345         
18346         this.el.on('click', this.onClick, this);
18347         
18348         if(Roo.isTouch){
18349             this.el.on("touchstart", this.onTouchStart, this);
18350             this.el.on("touchmove", this.onTouchMove, this);
18351             this.el.on("touchend", this.onTouchEnd, this);
18352         }
18353         
18354     },
18355     
18356     onRender : function(ct, position)
18357     {
18358         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18359     },
18360     
18361     setActive : function(state)
18362     {
18363         Roo.log("panel - set active " + this.tabId + "=" + state);
18364         
18365         this.active = state;
18366         if (!state) {
18367             this.el.removeClass('active');
18368             
18369         } else  if (!this.el.hasClass('active')) {
18370             this.el.addClass('active');
18371         }
18372         
18373         this.fireEvent('changed', this, state);
18374     },
18375     
18376     onClick : function(e)
18377     {
18378         e.preventDefault();
18379         
18380         if(!this.href.length){
18381             return;
18382         }
18383         
18384         window.location.href = this.href;
18385     },
18386     
18387     startX : 0,
18388     startY : 0,
18389     endX : 0,
18390     endY : 0,
18391     swiping : false,
18392     
18393     onTouchStart : function(e)
18394     {
18395         this.swiping = false;
18396         
18397         this.startX = e.browserEvent.touches[0].clientX;
18398         this.startY = e.browserEvent.touches[0].clientY;
18399     },
18400     
18401     onTouchMove : function(e)
18402     {
18403         this.swiping = true;
18404         
18405         this.endX = e.browserEvent.touches[0].clientX;
18406         this.endY = e.browserEvent.touches[0].clientY;
18407     },
18408     
18409     onTouchEnd : function(e)
18410     {
18411         if(!this.swiping){
18412             this.onClick(e);
18413             return;
18414         }
18415         
18416         var tabGroup = this.parent();
18417         
18418         if(this.endX > this.startX){ // swiping right
18419             tabGroup.showPanelPrev();
18420             return;
18421         }
18422         
18423         if(this.startX > this.endX){ // swiping left
18424             tabGroup.showPanelNext();
18425             return;
18426         }
18427     }
18428     
18429     
18430 });
18431  
18432
18433  
18434
18435  /*
18436  * - LGPL
18437  *
18438  * DateField
18439  * 
18440  */
18441
18442 /**
18443  * @class Roo.bootstrap.DateField
18444  * @extends Roo.bootstrap.Input
18445  * Bootstrap DateField class
18446  * @cfg {Number} weekStart default 0
18447  * @cfg {String} viewMode default empty, (months|years)
18448  * @cfg {String} minViewMode default empty, (months|years)
18449  * @cfg {Number} startDate default -Infinity
18450  * @cfg {Number} endDate default Infinity
18451  * @cfg {Boolean} todayHighlight default false
18452  * @cfg {Boolean} todayBtn default false
18453  * @cfg {Boolean} calendarWeeks default false
18454  * @cfg {Object} daysOfWeekDisabled default empty
18455  * @cfg {Boolean} singleMode default false (true | false)
18456  * 
18457  * @cfg {Boolean} keyboardNavigation default true
18458  * @cfg {String} language default en
18459  * 
18460  * @constructor
18461  * Create a new DateField
18462  * @param {Object} config The config object
18463  */
18464
18465 Roo.bootstrap.DateField = function(config){
18466     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18467      this.addEvents({
18468             /**
18469              * @event show
18470              * Fires when this field show.
18471              * @param {Roo.bootstrap.DateField} this
18472              * @param {Mixed} date The date value
18473              */
18474             show : true,
18475             /**
18476              * @event show
18477              * Fires when this field hide.
18478              * @param {Roo.bootstrap.DateField} this
18479              * @param {Mixed} date The date value
18480              */
18481             hide : true,
18482             /**
18483              * @event select
18484              * Fires when select a date.
18485              * @param {Roo.bootstrap.DateField} this
18486              * @param {Mixed} date The date value
18487              */
18488             select : true,
18489             /**
18490              * @event beforeselect
18491              * Fires when before select a date.
18492              * @param {Roo.bootstrap.DateField} this
18493              * @param {Mixed} date The date value
18494              */
18495             beforeselect : true
18496         });
18497 };
18498
18499 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18500     
18501     /**
18502      * @cfg {String} format
18503      * The default date format string which can be overriden for localization support.  The format must be
18504      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18505      */
18506     format : "m/d/y",
18507     /**
18508      * @cfg {String} altFormats
18509      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18510      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18511      */
18512     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18513     
18514     weekStart : 0,
18515     
18516     viewMode : '',
18517     
18518     minViewMode : '',
18519     
18520     todayHighlight : false,
18521     
18522     todayBtn: false,
18523     
18524     language: 'en',
18525     
18526     keyboardNavigation: true,
18527     
18528     calendarWeeks: false,
18529     
18530     startDate: -Infinity,
18531     
18532     endDate: Infinity,
18533     
18534     daysOfWeekDisabled: [],
18535     
18536     _events: [],
18537     
18538     singleMode : false,
18539     
18540     UTCDate: function()
18541     {
18542         return new Date(Date.UTC.apply(Date, arguments));
18543     },
18544     
18545     UTCToday: function()
18546     {
18547         var today = new Date();
18548         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18549     },
18550     
18551     getDate: function() {
18552             var d = this.getUTCDate();
18553             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18554     },
18555     
18556     getUTCDate: function() {
18557             return this.date;
18558     },
18559     
18560     setDate: function(d) {
18561             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18562     },
18563     
18564     setUTCDate: function(d) {
18565             this.date = d;
18566             this.setValue(this.formatDate(this.date));
18567     },
18568         
18569     onRender: function(ct, position)
18570     {
18571         
18572         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18573         
18574         this.language = this.language || 'en';
18575         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18576         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18577         
18578         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18579         this.format = this.format || 'm/d/y';
18580         this.isInline = false;
18581         this.isInput = true;
18582         this.component = this.el.select('.add-on', true).first() || false;
18583         this.component = (this.component && this.component.length === 0) ? false : this.component;
18584         this.hasInput = this.component && this.inputEl().length;
18585         
18586         if (typeof(this.minViewMode === 'string')) {
18587             switch (this.minViewMode) {
18588                 case 'months':
18589                     this.minViewMode = 1;
18590                     break;
18591                 case 'years':
18592                     this.minViewMode = 2;
18593                     break;
18594                 default:
18595                     this.minViewMode = 0;
18596                     break;
18597             }
18598         }
18599         
18600         if (typeof(this.viewMode === 'string')) {
18601             switch (this.viewMode) {
18602                 case 'months':
18603                     this.viewMode = 1;
18604                     break;
18605                 case 'years':
18606                     this.viewMode = 2;
18607                     break;
18608                 default:
18609                     this.viewMode = 0;
18610                     break;
18611             }
18612         }
18613                 
18614         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18615         
18616 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18617         
18618         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18619         
18620         this.picker().on('mousedown', this.onMousedown, this);
18621         this.picker().on('click', this.onClick, this);
18622         
18623         this.picker().addClass('datepicker-dropdown');
18624         
18625         this.startViewMode = this.viewMode;
18626         
18627         if(this.singleMode){
18628             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18629                 v.setVisibilityMode(Roo.Element.DISPLAY);
18630                 v.hide();
18631             });
18632             
18633             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18634                 v.setStyle('width', '189px');
18635             });
18636         }
18637         
18638         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18639             if(!this.calendarWeeks){
18640                 v.remove();
18641                 return;
18642             }
18643             
18644             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18645             v.attr('colspan', function(i, val){
18646                 return parseInt(val) + 1;
18647             });
18648         });
18649                         
18650         
18651         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18652         
18653         this.setStartDate(this.startDate);
18654         this.setEndDate(this.endDate);
18655         
18656         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18657         
18658         this.fillDow();
18659         this.fillMonths();
18660         this.update();
18661         this.showMode();
18662         
18663         if(this.isInline) {
18664             this.showPopup();
18665         }
18666     },
18667     
18668     picker : function()
18669     {
18670         return this.pickerEl;
18671 //        return this.el.select('.datepicker', true).first();
18672     },
18673     
18674     fillDow: function()
18675     {
18676         var dowCnt = this.weekStart;
18677         
18678         var dow = {
18679             tag: 'tr',
18680             cn: [
18681                 
18682             ]
18683         };
18684         
18685         if(this.calendarWeeks){
18686             dow.cn.push({
18687                 tag: 'th',
18688                 cls: 'cw',
18689                 html: '&nbsp;'
18690             })
18691         }
18692         
18693         while (dowCnt < this.weekStart + 7) {
18694             dow.cn.push({
18695                 tag: 'th',
18696                 cls: 'dow',
18697                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18698             });
18699         }
18700         
18701         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18702     },
18703     
18704     fillMonths: function()
18705     {    
18706         var i = 0;
18707         var months = this.picker().select('>.datepicker-months td', true).first();
18708         
18709         months.dom.innerHTML = '';
18710         
18711         while (i < 12) {
18712             var month = {
18713                 tag: 'span',
18714                 cls: 'month',
18715                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18716             };
18717             
18718             months.createChild(month);
18719         }
18720         
18721     },
18722     
18723     update: function()
18724     {
18725         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;
18726         
18727         if (this.date < this.startDate) {
18728             this.viewDate = new Date(this.startDate);
18729         } else if (this.date > this.endDate) {
18730             this.viewDate = new Date(this.endDate);
18731         } else {
18732             this.viewDate = new Date(this.date);
18733         }
18734         
18735         this.fill();
18736     },
18737     
18738     fill: function() 
18739     {
18740         var d = new Date(this.viewDate),
18741                 year = d.getUTCFullYear(),
18742                 month = d.getUTCMonth(),
18743                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18744                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18745                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18746                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18747                 currentDate = this.date && this.date.valueOf(),
18748                 today = this.UTCToday();
18749         
18750         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18751         
18752 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18753         
18754 //        this.picker.select('>tfoot th.today').
18755 //                                              .text(dates[this.language].today)
18756 //                                              .toggle(this.todayBtn !== false);
18757     
18758         this.updateNavArrows();
18759         this.fillMonths();
18760                                                 
18761         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18762         
18763         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18764          
18765         prevMonth.setUTCDate(day);
18766         
18767         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18768         
18769         var nextMonth = new Date(prevMonth);
18770         
18771         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18772         
18773         nextMonth = nextMonth.valueOf();
18774         
18775         var fillMonths = false;
18776         
18777         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18778         
18779         while(prevMonth.valueOf() <= nextMonth) {
18780             var clsName = '';
18781             
18782             if (prevMonth.getUTCDay() === this.weekStart) {
18783                 if(fillMonths){
18784                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18785                 }
18786                     
18787                 fillMonths = {
18788                     tag: 'tr',
18789                     cn: []
18790                 };
18791                 
18792                 if(this.calendarWeeks){
18793                     // ISO 8601: First week contains first thursday.
18794                     // ISO also states week starts on Monday, but we can be more abstract here.
18795                     var
18796                     // Start of current week: based on weekstart/current date
18797                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18798                     // Thursday of this week
18799                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18800                     // First Thursday of year, year from thursday
18801                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18802                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18803                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18804                     
18805                     fillMonths.cn.push({
18806                         tag: 'td',
18807                         cls: 'cw',
18808                         html: calWeek
18809                     });
18810                 }
18811             }
18812             
18813             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18814                 clsName += ' old';
18815             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18816                 clsName += ' new';
18817             }
18818             if (this.todayHighlight &&
18819                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18820                 prevMonth.getUTCMonth() == today.getMonth() &&
18821                 prevMonth.getUTCDate() == today.getDate()) {
18822                 clsName += ' today';
18823             }
18824             
18825             if (currentDate && prevMonth.valueOf() === currentDate) {
18826                 clsName += ' active';
18827             }
18828             
18829             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18830                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18831                     clsName += ' disabled';
18832             }
18833             
18834             fillMonths.cn.push({
18835                 tag: 'td',
18836                 cls: 'day ' + clsName,
18837                 html: prevMonth.getDate()
18838             });
18839             
18840             prevMonth.setDate(prevMonth.getDate()+1);
18841         }
18842           
18843         var currentYear = this.date && this.date.getUTCFullYear();
18844         var currentMonth = this.date && this.date.getUTCMonth();
18845         
18846         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18847         
18848         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18849             v.removeClass('active');
18850             
18851             if(currentYear === year && k === currentMonth){
18852                 v.addClass('active');
18853             }
18854             
18855             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18856                 v.addClass('disabled');
18857             }
18858             
18859         });
18860         
18861         
18862         year = parseInt(year/10, 10) * 10;
18863         
18864         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18865         
18866         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18867         
18868         year -= 1;
18869         for (var i = -1; i < 11; i++) {
18870             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18871                 tag: 'span',
18872                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18873                 html: year
18874             });
18875             
18876             year += 1;
18877         }
18878     },
18879     
18880     showMode: function(dir) 
18881     {
18882         if (dir) {
18883             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18884         }
18885         
18886         Roo.each(this.picker().select('>div',true).elements, function(v){
18887             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18888             v.hide();
18889         });
18890         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18891     },
18892     
18893     place: function()
18894     {
18895         if(this.isInline) {
18896             return;
18897         }
18898         
18899         this.picker().removeClass(['bottom', 'top']);
18900         
18901         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18902             /*
18903              * place to the top of element!
18904              *
18905              */
18906             
18907             this.picker().addClass('top');
18908             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18909             
18910             return;
18911         }
18912         
18913         this.picker().addClass('bottom');
18914         
18915         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18916     },
18917     
18918     parseDate : function(value)
18919     {
18920         if(!value || value instanceof Date){
18921             return value;
18922         }
18923         var v = Date.parseDate(value, this.format);
18924         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18925             v = Date.parseDate(value, 'Y-m-d');
18926         }
18927         if(!v && this.altFormats){
18928             if(!this.altFormatsArray){
18929                 this.altFormatsArray = this.altFormats.split("|");
18930             }
18931             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18932                 v = Date.parseDate(value, this.altFormatsArray[i]);
18933             }
18934         }
18935         return v;
18936     },
18937     
18938     formatDate : function(date, fmt)
18939     {   
18940         return (!date || !(date instanceof Date)) ?
18941         date : date.dateFormat(fmt || this.format);
18942     },
18943     
18944     onFocus : function()
18945     {
18946         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18947         this.showPopup();
18948     },
18949     
18950     onBlur : function()
18951     {
18952         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18953         
18954         var d = this.inputEl().getValue();
18955         
18956         this.setValue(d);
18957                 
18958         this.hidePopup();
18959     },
18960     
18961     showPopup : function()
18962     {
18963         this.picker().show();
18964         this.update();
18965         this.place();
18966         
18967         this.fireEvent('showpopup', this, this.date);
18968     },
18969     
18970     hidePopup : function()
18971     {
18972         if(this.isInline) {
18973             return;
18974         }
18975         this.picker().hide();
18976         this.viewMode = this.startViewMode;
18977         this.showMode();
18978         
18979         this.fireEvent('hidepopup', this, this.date);
18980         
18981     },
18982     
18983     onMousedown: function(e)
18984     {
18985         e.stopPropagation();
18986         e.preventDefault();
18987     },
18988     
18989     keyup: function(e)
18990     {
18991         Roo.bootstrap.DateField.superclass.keyup.call(this);
18992         this.update();
18993     },
18994
18995     setValue: function(v)
18996     {
18997         if(this.fireEvent('beforeselect', this, v) !== false){
18998             var d = new Date(this.parseDate(v) ).clearTime();
18999         
19000             if(isNaN(d.getTime())){
19001                 this.date = this.viewDate = '';
19002                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19003                 return;
19004             }
19005
19006             v = this.formatDate(d);
19007
19008             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19009
19010             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19011
19012             this.update();
19013
19014             this.fireEvent('select', this, this.date);
19015         }
19016     },
19017     
19018     getValue: function()
19019     {
19020         return this.formatDate(this.date);
19021     },
19022     
19023     fireKey: function(e)
19024     {
19025         if (!this.picker().isVisible()){
19026             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19027                 this.showPopup();
19028             }
19029             return;
19030         }
19031         
19032         var dateChanged = false,
19033         dir, day, month,
19034         newDate, newViewDate;
19035         
19036         switch(e.keyCode){
19037             case 27: // escape
19038                 this.hidePopup();
19039                 e.preventDefault();
19040                 break;
19041             case 37: // left
19042             case 39: // right
19043                 if (!this.keyboardNavigation) {
19044                     break;
19045                 }
19046                 dir = e.keyCode == 37 ? -1 : 1;
19047                 
19048                 if (e.ctrlKey){
19049                     newDate = this.moveYear(this.date, dir);
19050                     newViewDate = this.moveYear(this.viewDate, dir);
19051                 } else if (e.shiftKey){
19052                     newDate = this.moveMonth(this.date, dir);
19053                     newViewDate = this.moveMonth(this.viewDate, dir);
19054                 } else {
19055                     newDate = new Date(this.date);
19056                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19057                     newViewDate = new Date(this.viewDate);
19058                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19059                 }
19060                 if (this.dateWithinRange(newDate)){
19061                     this.date = newDate;
19062                     this.viewDate = newViewDate;
19063                     this.setValue(this.formatDate(this.date));
19064 //                    this.update();
19065                     e.preventDefault();
19066                     dateChanged = true;
19067                 }
19068                 break;
19069             case 38: // up
19070             case 40: // down
19071                 if (!this.keyboardNavigation) {
19072                     break;
19073                 }
19074                 dir = e.keyCode == 38 ? -1 : 1;
19075                 if (e.ctrlKey){
19076                     newDate = this.moveYear(this.date, dir);
19077                     newViewDate = this.moveYear(this.viewDate, dir);
19078                 } else if (e.shiftKey){
19079                     newDate = this.moveMonth(this.date, dir);
19080                     newViewDate = this.moveMonth(this.viewDate, dir);
19081                 } else {
19082                     newDate = new Date(this.date);
19083                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19084                     newViewDate = new Date(this.viewDate);
19085                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19086                 }
19087                 if (this.dateWithinRange(newDate)){
19088                     this.date = newDate;
19089                     this.viewDate = newViewDate;
19090                     this.setValue(this.formatDate(this.date));
19091 //                    this.update();
19092                     e.preventDefault();
19093                     dateChanged = true;
19094                 }
19095                 break;
19096             case 13: // enter
19097                 this.setValue(this.formatDate(this.date));
19098                 this.hidePopup();
19099                 e.preventDefault();
19100                 break;
19101             case 9: // tab
19102                 this.setValue(this.formatDate(this.date));
19103                 this.hidePopup();
19104                 break;
19105             case 16: // shift
19106             case 17: // ctrl
19107             case 18: // alt
19108                 break;
19109             default :
19110                 this.hide();
19111                 
19112         }
19113     },
19114     
19115     
19116     onClick: function(e) 
19117     {
19118         e.stopPropagation();
19119         e.preventDefault();
19120         
19121         var target = e.getTarget();
19122         
19123         if(target.nodeName.toLowerCase() === 'i'){
19124             target = Roo.get(target).dom.parentNode;
19125         }
19126         
19127         var nodeName = target.nodeName;
19128         var className = target.className;
19129         var html = target.innerHTML;
19130         //Roo.log(nodeName);
19131         
19132         switch(nodeName.toLowerCase()) {
19133             case 'th':
19134                 switch(className) {
19135                     case 'switch':
19136                         this.showMode(1);
19137                         break;
19138                     case 'prev':
19139                     case 'next':
19140                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19141                         switch(this.viewMode){
19142                                 case 0:
19143                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19144                                         break;
19145                                 case 1:
19146                                 case 2:
19147                                         this.viewDate = this.moveYear(this.viewDate, dir);
19148                                         break;
19149                         }
19150                         this.fill();
19151                         break;
19152                     case 'today':
19153                         var date = new Date();
19154                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19155 //                        this.fill()
19156                         this.setValue(this.formatDate(this.date));
19157                         
19158                         this.hidePopup();
19159                         break;
19160                 }
19161                 break;
19162             case 'span':
19163                 if (className.indexOf('disabled') < 0) {
19164                     this.viewDate.setUTCDate(1);
19165                     if (className.indexOf('month') > -1) {
19166                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19167                     } else {
19168                         var year = parseInt(html, 10) || 0;
19169                         this.viewDate.setUTCFullYear(year);
19170                         
19171                     }
19172                     
19173                     if(this.singleMode){
19174                         this.setValue(this.formatDate(this.viewDate));
19175                         this.hidePopup();
19176                         return;
19177                     }
19178                     
19179                     this.showMode(-1);
19180                     this.fill();
19181                 }
19182                 break;
19183                 
19184             case 'td':
19185                 //Roo.log(className);
19186                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19187                     var day = parseInt(html, 10) || 1;
19188                     var year = this.viewDate.getUTCFullYear(),
19189                         month = this.viewDate.getUTCMonth();
19190
19191                     if (className.indexOf('old') > -1) {
19192                         if(month === 0 ){
19193                             month = 11;
19194                             year -= 1;
19195                         }else{
19196                             month -= 1;
19197                         }
19198                     } else if (className.indexOf('new') > -1) {
19199                         if (month == 11) {
19200                             month = 0;
19201                             year += 1;
19202                         } else {
19203                             month += 1;
19204                         }
19205                     }
19206                     //Roo.log([year,month,day]);
19207                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19208                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19209 //                    this.fill();
19210                     //Roo.log(this.formatDate(this.date));
19211                     this.setValue(this.formatDate(this.date));
19212                     this.hidePopup();
19213                 }
19214                 break;
19215         }
19216     },
19217     
19218     setStartDate: function(startDate)
19219     {
19220         this.startDate = startDate || -Infinity;
19221         if (this.startDate !== -Infinity) {
19222             this.startDate = this.parseDate(this.startDate);
19223         }
19224         this.update();
19225         this.updateNavArrows();
19226     },
19227
19228     setEndDate: function(endDate)
19229     {
19230         this.endDate = endDate || Infinity;
19231         if (this.endDate !== Infinity) {
19232             this.endDate = this.parseDate(this.endDate);
19233         }
19234         this.update();
19235         this.updateNavArrows();
19236     },
19237     
19238     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19239     {
19240         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19241         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19242             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19243         }
19244         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19245             return parseInt(d, 10);
19246         });
19247         this.update();
19248         this.updateNavArrows();
19249     },
19250     
19251     updateNavArrows: function() 
19252     {
19253         if(this.singleMode){
19254             return;
19255         }
19256         
19257         var d = new Date(this.viewDate),
19258         year = d.getUTCFullYear(),
19259         month = d.getUTCMonth();
19260         
19261         Roo.each(this.picker().select('.prev', true).elements, function(v){
19262             v.show();
19263             switch (this.viewMode) {
19264                 case 0:
19265
19266                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19267                         v.hide();
19268                     }
19269                     break;
19270                 case 1:
19271                 case 2:
19272                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19273                         v.hide();
19274                     }
19275                     break;
19276             }
19277         });
19278         
19279         Roo.each(this.picker().select('.next', true).elements, function(v){
19280             v.show();
19281             switch (this.viewMode) {
19282                 case 0:
19283
19284                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19285                         v.hide();
19286                     }
19287                     break;
19288                 case 1:
19289                 case 2:
19290                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19291                         v.hide();
19292                     }
19293                     break;
19294             }
19295         })
19296     },
19297     
19298     moveMonth: function(date, dir)
19299     {
19300         if (!dir) {
19301             return date;
19302         }
19303         var new_date = new Date(date.valueOf()),
19304         day = new_date.getUTCDate(),
19305         month = new_date.getUTCMonth(),
19306         mag = Math.abs(dir),
19307         new_month, test;
19308         dir = dir > 0 ? 1 : -1;
19309         if (mag == 1){
19310             test = dir == -1
19311             // If going back one month, make sure month is not current month
19312             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19313             ? function(){
19314                 return new_date.getUTCMonth() == month;
19315             }
19316             // If going forward one month, make sure month is as expected
19317             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19318             : function(){
19319                 return new_date.getUTCMonth() != new_month;
19320             };
19321             new_month = month + dir;
19322             new_date.setUTCMonth(new_month);
19323             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19324             if (new_month < 0 || new_month > 11) {
19325                 new_month = (new_month + 12) % 12;
19326             }
19327         } else {
19328             // For magnitudes >1, move one month at a time...
19329             for (var i=0; i<mag; i++) {
19330                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19331                 new_date = this.moveMonth(new_date, dir);
19332             }
19333             // ...then reset the day, keeping it in the new month
19334             new_month = new_date.getUTCMonth();
19335             new_date.setUTCDate(day);
19336             test = function(){
19337                 return new_month != new_date.getUTCMonth();
19338             };
19339         }
19340         // Common date-resetting loop -- if date is beyond end of month, make it
19341         // end of month
19342         while (test()){
19343             new_date.setUTCDate(--day);
19344             new_date.setUTCMonth(new_month);
19345         }
19346         return new_date;
19347     },
19348
19349     moveYear: function(date, dir)
19350     {
19351         return this.moveMonth(date, dir*12);
19352     },
19353
19354     dateWithinRange: function(date)
19355     {
19356         return date >= this.startDate && date <= this.endDate;
19357     },
19358
19359     
19360     remove: function() 
19361     {
19362         this.picker().remove();
19363     },
19364     
19365     validateValue : function(value)
19366     {
19367         if(this.getVisibilityEl().hasClass('hidden')){
19368             return true;
19369         }
19370         
19371         if(value.length < 1)  {
19372             if(this.allowBlank){
19373                 return true;
19374             }
19375             return false;
19376         }
19377         
19378         if(value.length < this.minLength){
19379             return false;
19380         }
19381         if(value.length > this.maxLength){
19382             return false;
19383         }
19384         if(this.vtype){
19385             var vt = Roo.form.VTypes;
19386             if(!vt[this.vtype](value, this)){
19387                 return false;
19388             }
19389         }
19390         if(typeof this.validator == "function"){
19391             var msg = this.validator(value);
19392             if(msg !== true){
19393                 return false;
19394             }
19395         }
19396         
19397         if(this.regex && !this.regex.test(value)){
19398             return false;
19399         }
19400         
19401         if(typeof(this.parseDate(value)) == 'undefined'){
19402             return false;
19403         }
19404         
19405         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19406             return false;
19407         }      
19408         
19409         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19410             return false;
19411         } 
19412         
19413         
19414         return true;
19415     },
19416     
19417     reset : function()
19418     {
19419         this.date = this.viewDate = '';
19420         
19421         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19422     }
19423    
19424 });
19425
19426 Roo.apply(Roo.bootstrap.DateField,  {
19427     
19428     head : {
19429         tag: 'thead',
19430         cn: [
19431         {
19432             tag: 'tr',
19433             cn: [
19434             {
19435                 tag: 'th',
19436                 cls: 'prev',
19437                 html: '<i class="fa fa-arrow-left"/>'
19438             },
19439             {
19440                 tag: 'th',
19441                 cls: 'switch',
19442                 colspan: '5'
19443             },
19444             {
19445                 tag: 'th',
19446                 cls: 'next',
19447                 html: '<i class="fa fa-arrow-right"/>'
19448             }
19449
19450             ]
19451         }
19452         ]
19453     },
19454     
19455     content : {
19456         tag: 'tbody',
19457         cn: [
19458         {
19459             tag: 'tr',
19460             cn: [
19461             {
19462                 tag: 'td',
19463                 colspan: '7'
19464             }
19465             ]
19466         }
19467         ]
19468     },
19469     
19470     footer : {
19471         tag: 'tfoot',
19472         cn: [
19473         {
19474             tag: 'tr',
19475             cn: [
19476             {
19477                 tag: 'th',
19478                 colspan: '7',
19479                 cls: 'today'
19480             }
19481                     
19482             ]
19483         }
19484         ]
19485     },
19486     
19487     dates:{
19488         en: {
19489             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19490             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19491             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19492             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19493             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19494             today: "Today"
19495         }
19496     },
19497     
19498     modes: [
19499     {
19500         clsName: 'days',
19501         navFnc: 'Month',
19502         navStep: 1
19503     },
19504     {
19505         clsName: 'months',
19506         navFnc: 'FullYear',
19507         navStep: 1
19508     },
19509     {
19510         clsName: 'years',
19511         navFnc: 'FullYear',
19512         navStep: 10
19513     }]
19514 });
19515
19516 Roo.apply(Roo.bootstrap.DateField,  {
19517   
19518     template : {
19519         tag: 'div',
19520         cls: 'datepicker dropdown-menu roo-dynamic',
19521         cn: [
19522         {
19523             tag: 'div',
19524             cls: 'datepicker-days',
19525             cn: [
19526             {
19527                 tag: 'table',
19528                 cls: 'table-condensed',
19529                 cn:[
19530                 Roo.bootstrap.DateField.head,
19531                 {
19532                     tag: 'tbody'
19533                 },
19534                 Roo.bootstrap.DateField.footer
19535                 ]
19536             }
19537             ]
19538         },
19539         {
19540             tag: 'div',
19541             cls: 'datepicker-months',
19542             cn: [
19543             {
19544                 tag: 'table',
19545                 cls: 'table-condensed',
19546                 cn:[
19547                 Roo.bootstrap.DateField.head,
19548                 Roo.bootstrap.DateField.content,
19549                 Roo.bootstrap.DateField.footer
19550                 ]
19551             }
19552             ]
19553         },
19554         {
19555             tag: 'div',
19556             cls: 'datepicker-years',
19557             cn: [
19558             {
19559                 tag: 'table',
19560                 cls: 'table-condensed',
19561                 cn:[
19562                 Roo.bootstrap.DateField.head,
19563                 Roo.bootstrap.DateField.content,
19564                 Roo.bootstrap.DateField.footer
19565                 ]
19566             }
19567             ]
19568         }
19569         ]
19570     }
19571 });
19572
19573  
19574
19575  /*
19576  * - LGPL
19577  *
19578  * TimeField
19579  * 
19580  */
19581
19582 /**
19583  * @class Roo.bootstrap.TimeField
19584  * @extends Roo.bootstrap.Input
19585  * Bootstrap DateField class
19586  * 
19587  * 
19588  * @constructor
19589  * Create a new TimeField
19590  * @param {Object} config The config object
19591  */
19592
19593 Roo.bootstrap.TimeField = function(config){
19594     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19595     this.addEvents({
19596             /**
19597              * @event show
19598              * Fires when this field show.
19599              * @param {Roo.bootstrap.DateField} thisthis
19600              * @param {Mixed} date The date value
19601              */
19602             show : true,
19603             /**
19604              * @event show
19605              * Fires when this field hide.
19606              * @param {Roo.bootstrap.DateField} this
19607              * @param {Mixed} date The date value
19608              */
19609             hide : true,
19610             /**
19611              * @event select
19612              * Fires when select a date.
19613              * @param {Roo.bootstrap.DateField} this
19614              * @param {Mixed} date The date value
19615              */
19616             select : true
19617         });
19618 };
19619
19620 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19621     
19622     /**
19623      * @cfg {String} format
19624      * The default time format string which can be overriden for localization support.  The format must be
19625      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19626      */
19627     format : "H:i",
19628        
19629     onRender: function(ct, position)
19630     {
19631         
19632         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19633                 
19634         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19635         
19636         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19637         
19638         this.pop = this.picker().select('>.datepicker-time',true).first();
19639         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19640         
19641         this.picker().on('mousedown', this.onMousedown, this);
19642         this.picker().on('click', this.onClick, this);
19643         
19644         this.picker().addClass('datepicker-dropdown');
19645     
19646         this.fillTime();
19647         this.update();
19648             
19649         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19650         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19651         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19652         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19653         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19654         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19655
19656     },
19657     
19658     fireKey: function(e){
19659         if (!this.picker().isVisible()){
19660             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19661                 this.show();
19662             }
19663             return;
19664         }
19665
19666         e.preventDefault();
19667         
19668         switch(e.keyCode){
19669             case 27: // escape
19670                 this.hide();
19671                 break;
19672             case 37: // left
19673             case 39: // right
19674                 this.onTogglePeriod();
19675                 break;
19676             case 38: // up
19677                 this.onIncrementMinutes();
19678                 break;
19679             case 40: // down
19680                 this.onDecrementMinutes();
19681                 break;
19682             case 13: // enter
19683             case 9: // tab
19684                 this.setTime();
19685                 break;
19686         }
19687     },
19688     
19689     onClick: function(e) {
19690         e.stopPropagation();
19691         e.preventDefault();
19692     },
19693     
19694     picker : function()
19695     {
19696         return this.el.select('.datepicker', true).first();
19697     },
19698     
19699     fillTime: function()
19700     {    
19701         var time = this.pop.select('tbody', true).first();
19702         
19703         time.dom.innerHTML = '';
19704         
19705         time.createChild({
19706             tag: 'tr',
19707             cn: [
19708                 {
19709                     tag: 'td',
19710                     cn: [
19711                         {
19712                             tag: 'a',
19713                             href: '#',
19714                             cls: 'btn',
19715                             cn: [
19716                                 {
19717                                     tag: 'span',
19718                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19719                                 }
19720                             ]
19721                         } 
19722                     ]
19723                 },
19724                 {
19725                     tag: 'td',
19726                     cls: 'separator'
19727                 },
19728                 {
19729                     tag: 'td',
19730                     cn: [
19731                         {
19732                             tag: 'a',
19733                             href: '#',
19734                             cls: 'btn',
19735                             cn: [
19736                                 {
19737                                     tag: 'span',
19738                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19739                                 }
19740                             ]
19741                         }
19742                     ]
19743                 },
19744                 {
19745                     tag: 'td',
19746                     cls: 'separator'
19747                 }
19748             ]
19749         });
19750         
19751         time.createChild({
19752             tag: 'tr',
19753             cn: [
19754                 {
19755                     tag: 'td',
19756                     cn: [
19757                         {
19758                             tag: 'span',
19759                             cls: 'timepicker-hour',
19760                             html: '00'
19761                         }  
19762                     ]
19763                 },
19764                 {
19765                     tag: 'td',
19766                     cls: 'separator',
19767                     html: ':'
19768                 },
19769                 {
19770                     tag: 'td',
19771                     cn: [
19772                         {
19773                             tag: 'span',
19774                             cls: 'timepicker-minute',
19775                             html: '00'
19776                         }  
19777                     ]
19778                 },
19779                 {
19780                     tag: 'td',
19781                     cls: 'separator'
19782                 },
19783                 {
19784                     tag: 'td',
19785                     cn: [
19786                         {
19787                             tag: 'button',
19788                             type: 'button',
19789                             cls: 'btn btn-primary period',
19790                             html: 'AM'
19791                             
19792                         }
19793                     ]
19794                 }
19795             ]
19796         });
19797         
19798         time.createChild({
19799             tag: 'tr',
19800             cn: [
19801                 {
19802                     tag: 'td',
19803                     cn: [
19804                         {
19805                             tag: 'a',
19806                             href: '#',
19807                             cls: 'btn',
19808                             cn: [
19809                                 {
19810                                     tag: 'span',
19811                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19812                                 }
19813                             ]
19814                         }
19815                     ]
19816                 },
19817                 {
19818                     tag: 'td',
19819                     cls: 'separator'
19820                 },
19821                 {
19822                     tag: 'td',
19823                     cn: [
19824                         {
19825                             tag: 'a',
19826                             href: '#',
19827                             cls: 'btn',
19828                             cn: [
19829                                 {
19830                                     tag: 'span',
19831                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19832                                 }
19833                             ]
19834                         }
19835                     ]
19836                 },
19837                 {
19838                     tag: 'td',
19839                     cls: 'separator'
19840                 }
19841             ]
19842         });
19843         
19844     },
19845     
19846     update: function()
19847     {
19848         
19849         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19850         
19851         this.fill();
19852     },
19853     
19854     fill: function() 
19855     {
19856         var hours = this.time.getHours();
19857         var minutes = this.time.getMinutes();
19858         var period = 'AM';
19859         
19860         if(hours > 11){
19861             period = 'PM';
19862         }
19863         
19864         if(hours == 0){
19865             hours = 12;
19866         }
19867         
19868         
19869         if(hours > 12){
19870             hours = hours - 12;
19871         }
19872         
19873         if(hours < 10){
19874             hours = '0' + hours;
19875         }
19876         
19877         if(minutes < 10){
19878             minutes = '0' + minutes;
19879         }
19880         
19881         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19882         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19883         this.pop.select('button', true).first().dom.innerHTML = period;
19884         
19885     },
19886     
19887     place: function()
19888     {   
19889         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19890         
19891         var cls = ['bottom'];
19892         
19893         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19894             cls.pop();
19895             cls.push('top');
19896         }
19897         
19898         cls.push('right');
19899         
19900         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19901             cls.pop();
19902             cls.push('left');
19903         }
19904         
19905         this.picker().addClass(cls.join('-'));
19906         
19907         var _this = this;
19908         
19909         Roo.each(cls, function(c){
19910             if(c == 'bottom'){
19911                 _this.picker().setTop(_this.inputEl().getHeight());
19912                 return;
19913             }
19914             if(c == 'top'){
19915                 _this.picker().setTop(0 - _this.picker().getHeight());
19916                 return;
19917             }
19918             
19919             if(c == 'left'){
19920                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19921                 return;
19922             }
19923             if(c == 'right'){
19924                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19925                 return;
19926             }
19927         });
19928         
19929     },
19930   
19931     onFocus : function()
19932     {
19933         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19934         this.show();
19935     },
19936     
19937     onBlur : function()
19938     {
19939         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19940         this.hide();
19941     },
19942     
19943     show : function()
19944     {
19945         this.picker().show();
19946         this.pop.show();
19947         this.update();
19948         this.place();
19949         
19950         this.fireEvent('show', this, this.date);
19951     },
19952     
19953     hide : function()
19954     {
19955         this.picker().hide();
19956         this.pop.hide();
19957         
19958         this.fireEvent('hide', this, this.date);
19959     },
19960     
19961     setTime : function()
19962     {
19963         this.hide();
19964         this.setValue(this.time.format(this.format));
19965         
19966         this.fireEvent('select', this, this.date);
19967         
19968         
19969     },
19970     
19971     onMousedown: function(e){
19972         e.stopPropagation();
19973         e.preventDefault();
19974     },
19975     
19976     onIncrementHours: function()
19977     {
19978         Roo.log('onIncrementHours');
19979         this.time = this.time.add(Date.HOUR, 1);
19980         this.update();
19981         
19982     },
19983     
19984     onDecrementHours: function()
19985     {
19986         Roo.log('onDecrementHours');
19987         this.time = this.time.add(Date.HOUR, -1);
19988         this.update();
19989     },
19990     
19991     onIncrementMinutes: function()
19992     {
19993         Roo.log('onIncrementMinutes');
19994         this.time = this.time.add(Date.MINUTE, 1);
19995         this.update();
19996     },
19997     
19998     onDecrementMinutes: function()
19999     {
20000         Roo.log('onDecrementMinutes');
20001         this.time = this.time.add(Date.MINUTE, -1);
20002         this.update();
20003     },
20004     
20005     onTogglePeriod: function()
20006     {
20007         Roo.log('onTogglePeriod');
20008         this.time = this.time.add(Date.HOUR, 12);
20009         this.update();
20010     }
20011     
20012    
20013 });
20014
20015 Roo.apply(Roo.bootstrap.TimeField,  {
20016     
20017     content : {
20018         tag: 'tbody',
20019         cn: [
20020             {
20021                 tag: 'tr',
20022                 cn: [
20023                 {
20024                     tag: 'td',
20025                     colspan: '7'
20026                 }
20027                 ]
20028             }
20029         ]
20030     },
20031     
20032     footer : {
20033         tag: 'tfoot',
20034         cn: [
20035             {
20036                 tag: 'tr',
20037                 cn: [
20038                 {
20039                     tag: 'th',
20040                     colspan: '7',
20041                     cls: '',
20042                     cn: [
20043                         {
20044                             tag: 'button',
20045                             cls: 'btn btn-info ok',
20046                             html: 'OK'
20047                         }
20048                     ]
20049                 }
20050
20051                 ]
20052             }
20053         ]
20054     }
20055 });
20056
20057 Roo.apply(Roo.bootstrap.TimeField,  {
20058   
20059     template : {
20060         tag: 'div',
20061         cls: 'datepicker dropdown-menu',
20062         cn: [
20063             {
20064                 tag: 'div',
20065                 cls: 'datepicker-time',
20066                 cn: [
20067                 {
20068                     tag: 'table',
20069                     cls: 'table-condensed',
20070                     cn:[
20071                     Roo.bootstrap.TimeField.content,
20072                     Roo.bootstrap.TimeField.footer
20073                     ]
20074                 }
20075                 ]
20076             }
20077         ]
20078     }
20079 });
20080
20081  
20082
20083  /*
20084  * - LGPL
20085  *
20086  * MonthField
20087  * 
20088  */
20089
20090 /**
20091  * @class Roo.bootstrap.MonthField
20092  * @extends Roo.bootstrap.Input
20093  * Bootstrap MonthField class
20094  * 
20095  * @cfg {String} language default en
20096  * 
20097  * @constructor
20098  * Create a new MonthField
20099  * @param {Object} config The config object
20100  */
20101
20102 Roo.bootstrap.MonthField = function(config){
20103     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20104     
20105     this.addEvents({
20106         /**
20107          * @event show
20108          * Fires when this field show.
20109          * @param {Roo.bootstrap.MonthField} this
20110          * @param {Mixed} date The date value
20111          */
20112         show : true,
20113         /**
20114          * @event show
20115          * Fires when this field hide.
20116          * @param {Roo.bootstrap.MonthField} this
20117          * @param {Mixed} date The date value
20118          */
20119         hide : true,
20120         /**
20121          * @event select
20122          * Fires when select a date.
20123          * @param {Roo.bootstrap.MonthField} this
20124          * @param {String} oldvalue The old value
20125          * @param {String} newvalue The new value
20126          */
20127         select : true
20128     });
20129 };
20130
20131 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20132     
20133     onRender: function(ct, position)
20134     {
20135         
20136         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20137         
20138         this.language = this.language || 'en';
20139         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20140         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20141         
20142         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20143         this.isInline = false;
20144         this.isInput = true;
20145         this.component = this.el.select('.add-on', true).first() || false;
20146         this.component = (this.component && this.component.length === 0) ? false : this.component;
20147         this.hasInput = this.component && this.inputEL().length;
20148         
20149         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20150         
20151         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20152         
20153         this.picker().on('mousedown', this.onMousedown, this);
20154         this.picker().on('click', this.onClick, this);
20155         
20156         this.picker().addClass('datepicker-dropdown');
20157         
20158         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20159             v.setStyle('width', '189px');
20160         });
20161         
20162         this.fillMonths();
20163         
20164         this.update();
20165         
20166         if(this.isInline) {
20167             this.show();
20168         }
20169         
20170     },
20171     
20172     setValue: function(v, suppressEvent)
20173     {   
20174         var o = this.getValue();
20175         
20176         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20177         
20178         this.update();
20179
20180         if(suppressEvent !== true){
20181             this.fireEvent('select', this, o, v);
20182         }
20183         
20184     },
20185     
20186     getValue: function()
20187     {
20188         return this.value;
20189     },
20190     
20191     onClick: function(e) 
20192     {
20193         e.stopPropagation();
20194         e.preventDefault();
20195         
20196         var target = e.getTarget();
20197         
20198         if(target.nodeName.toLowerCase() === 'i'){
20199             target = Roo.get(target).dom.parentNode;
20200         }
20201         
20202         var nodeName = target.nodeName;
20203         var className = target.className;
20204         var html = target.innerHTML;
20205         
20206         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20207             return;
20208         }
20209         
20210         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20211         
20212         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20213         
20214         this.hide();
20215                         
20216     },
20217     
20218     picker : function()
20219     {
20220         return this.pickerEl;
20221     },
20222     
20223     fillMonths: function()
20224     {    
20225         var i = 0;
20226         var months = this.picker().select('>.datepicker-months td', true).first();
20227         
20228         months.dom.innerHTML = '';
20229         
20230         while (i < 12) {
20231             var month = {
20232                 tag: 'span',
20233                 cls: 'month',
20234                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20235             };
20236             
20237             months.createChild(month);
20238         }
20239         
20240     },
20241     
20242     update: function()
20243     {
20244         var _this = this;
20245         
20246         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20247             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20248         }
20249         
20250         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20251             e.removeClass('active');
20252             
20253             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20254                 e.addClass('active');
20255             }
20256         })
20257     },
20258     
20259     place: function()
20260     {
20261         if(this.isInline) {
20262             return;
20263         }
20264         
20265         this.picker().removeClass(['bottom', 'top']);
20266         
20267         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20268             /*
20269              * place to the top of element!
20270              *
20271              */
20272             
20273             this.picker().addClass('top');
20274             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20275             
20276             return;
20277         }
20278         
20279         this.picker().addClass('bottom');
20280         
20281         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20282     },
20283     
20284     onFocus : function()
20285     {
20286         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20287         this.show();
20288     },
20289     
20290     onBlur : function()
20291     {
20292         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20293         
20294         var d = this.inputEl().getValue();
20295         
20296         this.setValue(d);
20297                 
20298         this.hide();
20299     },
20300     
20301     show : function()
20302     {
20303         this.picker().show();
20304         this.picker().select('>.datepicker-months', true).first().show();
20305         this.update();
20306         this.place();
20307         
20308         this.fireEvent('show', this, this.date);
20309     },
20310     
20311     hide : function()
20312     {
20313         if(this.isInline) {
20314             return;
20315         }
20316         this.picker().hide();
20317         this.fireEvent('hide', this, this.date);
20318         
20319     },
20320     
20321     onMousedown: function(e)
20322     {
20323         e.stopPropagation();
20324         e.preventDefault();
20325     },
20326     
20327     keyup: function(e)
20328     {
20329         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20330         this.update();
20331     },
20332
20333     fireKey: function(e)
20334     {
20335         if (!this.picker().isVisible()){
20336             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20337                 this.show();
20338             }
20339             return;
20340         }
20341         
20342         var dir;
20343         
20344         switch(e.keyCode){
20345             case 27: // escape
20346                 this.hide();
20347                 e.preventDefault();
20348                 break;
20349             case 37: // left
20350             case 39: // right
20351                 dir = e.keyCode == 37 ? -1 : 1;
20352                 
20353                 this.vIndex = this.vIndex + dir;
20354                 
20355                 if(this.vIndex < 0){
20356                     this.vIndex = 0;
20357                 }
20358                 
20359                 if(this.vIndex > 11){
20360                     this.vIndex = 11;
20361                 }
20362                 
20363                 if(isNaN(this.vIndex)){
20364                     this.vIndex = 0;
20365                 }
20366                 
20367                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20368                 
20369                 break;
20370             case 38: // up
20371             case 40: // down
20372                 
20373                 dir = e.keyCode == 38 ? -1 : 1;
20374                 
20375                 this.vIndex = this.vIndex + dir * 4;
20376                 
20377                 if(this.vIndex < 0){
20378                     this.vIndex = 0;
20379                 }
20380                 
20381                 if(this.vIndex > 11){
20382                     this.vIndex = 11;
20383                 }
20384                 
20385                 if(isNaN(this.vIndex)){
20386                     this.vIndex = 0;
20387                 }
20388                 
20389                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20390                 break;
20391                 
20392             case 13: // enter
20393                 
20394                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20395                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20396                 }
20397                 
20398                 this.hide();
20399                 e.preventDefault();
20400                 break;
20401             case 9: // tab
20402                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20403                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20404                 }
20405                 this.hide();
20406                 break;
20407             case 16: // shift
20408             case 17: // ctrl
20409             case 18: // alt
20410                 break;
20411             default :
20412                 this.hide();
20413                 
20414         }
20415     },
20416     
20417     remove: function() 
20418     {
20419         this.picker().remove();
20420     }
20421    
20422 });
20423
20424 Roo.apply(Roo.bootstrap.MonthField,  {
20425     
20426     content : {
20427         tag: 'tbody',
20428         cn: [
20429         {
20430             tag: 'tr',
20431             cn: [
20432             {
20433                 tag: 'td',
20434                 colspan: '7'
20435             }
20436             ]
20437         }
20438         ]
20439     },
20440     
20441     dates:{
20442         en: {
20443             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20444             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20445         }
20446     }
20447 });
20448
20449 Roo.apply(Roo.bootstrap.MonthField,  {
20450   
20451     template : {
20452         tag: 'div',
20453         cls: 'datepicker dropdown-menu roo-dynamic',
20454         cn: [
20455             {
20456                 tag: 'div',
20457                 cls: 'datepicker-months',
20458                 cn: [
20459                 {
20460                     tag: 'table',
20461                     cls: 'table-condensed',
20462                     cn:[
20463                         Roo.bootstrap.DateField.content
20464                     ]
20465                 }
20466                 ]
20467             }
20468         ]
20469     }
20470 });
20471
20472  
20473
20474  
20475  /*
20476  * - LGPL
20477  *
20478  * CheckBox
20479  * 
20480  */
20481
20482 /**
20483  * @class Roo.bootstrap.CheckBox
20484  * @extends Roo.bootstrap.Input
20485  * Bootstrap CheckBox class
20486  * 
20487  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20488  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20489  * @cfg {String} boxLabel The text that appears beside the checkbox
20490  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20491  * @cfg {Boolean} checked initnal the element
20492  * @cfg {Boolean} inline inline the element (default false)
20493  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20494  * @cfg {String} tooltip label tooltip
20495  * 
20496  * @constructor
20497  * Create a new CheckBox
20498  * @param {Object} config The config object
20499  */
20500
20501 Roo.bootstrap.CheckBox = function(config){
20502     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20503    
20504     this.addEvents({
20505         /**
20506         * @event check
20507         * Fires when the element is checked or unchecked.
20508         * @param {Roo.bootstrap.CheckBox} this This input
20509         * @param {Boolean} checked The new checked value
20510         */
20511        check : true,
20512        /**
20513         * @event click
20514         * Fires when the element is click.
20515         * @param {Roo.bootstrap.CheckBox} this This input
20516         */
20517        click : true
20518     });
20519     
20520 };
20521
20522 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20523   
20524     inputType: 'checkbox',
20525     inputValue: 1,
20526     valueOff: 0,
20527     boxLabel: false,
20528     checked: false,
20529     weight : false,
20530     inline: false,
20531     tooltip : '',
20532     
20533     getAutoCreate : function()
20534     {
20535         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20536         
20537         var id = Roo.id();
20538         
20539         var cfg = {};
20540         
20541         cfg.cls = 'form-group ' + this.inputType; //input-group
20542         
20543         if(this.inline){
20544             cfg.cls += ' ' + this.inputType + '-inline';
20545         }
20546         
20547         var input =  {
20548             tag: 'input',
20549             id : id,
20550             type : this.inputType,
20551             value : this.inputValue,
20552             cls : 'roo-' + this.inputType, //'form-box',
20553             placeholder : this.placeholder || ''
20554             
20555         };
20556         
20557         if(this.inputType != 'radio'){
20558             var hidden =  {
20559                 tag: 'input',
20560                 type : 'hidden',
20561                 cls : 'roo-hidden-value',
20562                 value : this.checked ? this.inputValue : this.valueOff
20563             };
20564         }
20565         
20566             
20567         if (this.weight) { // Validity check?
20568             cfg.cls += " " + this.inputType + "-" + this.weight;
20569         }
20570         
20571         if (this.disabled) {
20572             input.disabled=true;
20573         }
20574         
20575         if(this.checked){
20576             input.checked = this.checked;
20577         }
20578         
20579         if (this.name) {
20580             
20581             input.name = this.name;
20582             
20583             if(this.inputType != 'radio'){
20584                 hidden.name = this.name;
20585                 input.name = '_hidden_' + this.name;
20586             }
20587         }
20588         
20589         if (this.size) {
20590             input.cls += ' input-' + this.size;
20591         }
20592         
20593         var settings=this;
20594         
20595         ['xs','sm','md','lg'].map(function(size){
20596             if (settings[size]) {
20597                 cfg.cls += ' col-' + size + '-' + settings[size];
20598             }
20599         });
20600         
20601         var inputblock = input;
20602          
20603         if (this.before || this.after) {
20604             
20605             inputblock = {
20606                 cls : 'input-group',
20607                 cn :  [] 
20608             };
20609             
20610             if (this.before) {
20611                 inputblock.cn.push({
20612                     tag :'span',
20613                     cls : 'input-group-addon',
20614                     html : this.before
20615                 });
20616             }
20617             
20618             inputblock.cn.push(input);
20619             
20620             if(this.inputType != 'radio'){
20621                 inputblock.cn.push(hidden);
20622             }
20623             
20624             if (this.after) {
20625                 inputblock.cn.push({
20626                     tag :'span',
20627                     cls : 'input-group-addon',
20628                     html : this.after
20629                 });
20630             }
20631             
20632         }
20633         
20634         if (align ==='left' && this.fieldLabel.length) {
20635 //                Roo.log("left and has label");
20636             cfg.cn = [
20637                 {
20638                     tag: 'label',
20639                     'for' :  id,
20640                     cls : 'control-label',
20641                     html : this.fieldLabel
20642                 },
20643                 {
20644                     cls : "", 
20645                     cn: [
20646                         inputblock
20647                     ]
20648                 }
20649             ];
20650             
20651             if(this.labelWidth > 12){
20652                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20653             }
20654             
20655             if(this.labelWidth < 13 && this.labelmd == 0){
20656                 this.labelmd = this.labelWidth;
20657             }
20658             
20659             if(this.labellg > 0){
20660                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20661                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20662             }
20663             
20664             if(this.labelmd > 0){
20665                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20666                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20667             }
20668             
20669             if(this.labelsm > 0){
20670                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20671                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20672             }
20673             
20674             if(this.labelxs > 0){
20675                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20676                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20677             }
20678             
20679         } else if ( this.fieldLabel.length) {
20680 //                Roo.log(" label");
20681                 cfg.cn = [
20682                    
20683                     {
20684                         tag: this.boxLabel ? 'span' : 'label',
20685                         'for': id,
20686                         cls: 'control-label box-input-label',
20687                         //cls : 'input-group-addon',
20688                         html : this.fieldLabel
20689                     },
20690                     
20691                     inputblock
20692                     
20693                 ];
20694
20695         } else {
20696             
20697 //                Roo.log(" no label && no align");
20698                 cfg.cn = [  inputblock ] ;
20699                 
20700                 
20701         }
20702         
20703         if(this.boxLabel){
20704              var boxLabelCfg = {
20705                 tag: 'label',
20706                 //'for': id, // box label is handled by onclick - so no for...
20707                 cls: 'box-label',
20708                 html: this.boxLabel
20709             };
20710             
20711             if(this.tooltip){
20712                 boxLabelCfg.tooltip = this.tooltip;
20713             }
20714              
20715             cfg.cn.push(boxLabelCfg);
20716         }
20717         
20718         if(this.inputType != 'radio'){
20719             cfg.cn.push(hidden);
20720         }
20721         
20722         return cfg;
20723         
20724     },
20725     
20726     /**
20727      * return the real input element.
20728      */
20729     inputEl: function ()
20730     {
20731         return this.el.select('input.roo-' + this.inputType,true).first();
20732     },
20733     hiddenEl: function ()
20734     {
20735         return this.el.select('input.roo-hidden-value',true).first();
20736     },
20737     
20738     labelEl: function()
20739     {
20740         return this.el.select('label.control-label',true).first();
20741     },
20742     /* depricated... */
20743     
20744     label: function()
20745     {
20746         return this.labelEl();
20747     },
20748     
20749     boxLabelEl: function()
20750     {
20751         return this.el.select('label.box-label',true).first();
20752     },
20753     
20754     initEvents : function()
20755     {
20756 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20757         
20758         this.inputEl().on('click', this.onClick,  this);
20759         
20760         if (this.boxLabel) { 
20761             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20762         }
20763         
20764         this.startValue = this.getValue();
20765         
20766         if(this.groupId){
20767             Roo.bootstrap.CheckBox.register(this);
20768         }
20769     },
20770     
20771     onClick : function(e)
20772     {   
20773         if(this.fireEvent('click', this, e) !== false){
20774             this.setChecked(!this.checked);
20775         }
20776         
20777     },
20778     
20779     setChecked : function(state,suppressEvent)
20780     {
20781         this.startValue = this.getValue();
20782
20783         if(this.inputType == 'radio'){
20784             
20785             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20786                 e.dom.checked = false;
20787             });
20788             
20789             this.inputEl().dom.checked = true;
20790             
20791             this.inputEl().dom.value = this.inputValue;
20792             
20793             if(suppressEvent !== true){
20794                 this.fireEvent('check', this, true);
20795             }
20796             
20797             this.validate();
20798             
20799             return;
20800         }
20801         
20802         this.checked = state;
20803         
20804         this.inputEl().dom.checked = state;
20805         
20806         
20807         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20808         
20809         if(suppressEvent !== true){
20810             this.fireEvent('check', this, state);
20811         }
20812         
20813         this.validate();
20814     },
20815     
20816     getValue : function()
20817     {
20818         if(this.inputType == 'radio'){
20819             return this.getGroupValue();
20820         }
20821         
20822         return this.hiddenEl().dom.value;
20823         
20824     },
20825     
20826     getGroupValue : function()
20827     {
20828         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20829             return '';
20830         }
20831         
20832         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20833     },
20834     
20835     setValue : function(v,suppressEvent)
20836     {
20837         if(this.inputType == 'radio'){
20838             this.setGroupValue(v, suppressEvent);
20839             return;
20840         }
20841         
20842         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20843         
20844         this.validate();
20845     },
20846     
20847     setGroupValue : function(v, suppressEvent)
20848     {
20849         this.startValue = this.getValue();
20850         
20851         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20852             e.dom.checked = false;
20853             
20854             if(e.dom.value == v){
20855                 e.dom.checked = true;
20856             }
20857         });
20858         
20859         if(suppressEvent !== true){
20860             this.fireEvent('check', this, true);
20861         }
20862
20863         this.validate();
20864         
20865         return;
20866     },
20867     
20868     validate : function()
20869     {
20870         if(this.getVisibilityEl().hasClass('hidden')){
20871             return true;
20872         }
20873         
20874         if(
20875                 this.disabled || 
20876                 (this.inputType == 'radio' && this.validateRadio()) ||
20877                 (this.inputType == 'checkbox' && this.validateCheckbox())
20878         ){
20879             this.markValid();
20880             return true;
20881         }
20882         
20883         this.markInvalid();
20884         return false;
20885     },
20886     
20887     validateRadio : function()
20888     {
20889         if(this.getVisibilityEl().hasClass('hidden')){
20890             return true;
20891         }
20892         
20893         if(this.allowBlank){
20894             return true;
20895         }
20896         
20897         var valid = false;
20898         
20899         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20900             if(!e.dom.checked){
20901                 return;
20902             }
20903             
20904             valid = true;
20905             
20906             return false;
20907         });
20908         
20909         return valid;
20910     },
20911     
20912     validateCheckbox : function()
20913     {
20914         if(!this.groupId){
20915             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20916             //return (this.getValue() == this.inputValue) ? true : false;
20917         }
20918         
20919         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20920         
20921         if(!group){
20922             return false;
20923         }
20924         
20925         var r = false;
20926         
20927         for(var i in group){
20928             if(group[i].el.isVisible(true)){
20929                 r = false;
20930                 break;
20931             }
20932             
20933             r = true;
20934         }
20935         
20936         for(var i in group){
20937             if(r){
20938                 break;
20939             }
20940             
20941             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20942         }
20943         
20944         return r;
20945     },
20946     
20947     /**
20948      * Mark this field as valid
20949      */
20950     markValid : function()
20951     {
20952         var _this = this;
20953         
20954         this.fireEvent('valid', this);
20955         
20956         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20957         
20958         if(this.groupId){
20959             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20960         }
20961         
20962         if(label){
20963             label.markValid();
20964         }
20965
20966         if(this.inputType == 'radio'){
20967             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20968                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20969                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20970             });
20971             
20972             return;
20973         }
20974
20975         if(!this.groupId){
20976             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20977             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20978             return;
20979         }
20980         
20981         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20982         
20983         if(!group){
20984             return;
20985         }
20986         
20987         for(var i in group){
20988             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20989             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20990         }
20991     },
20992     
20993      /**
20994      * Mark this field as invalid
20995      * @param {String} msg The validation message
20996      */
20997     markInvalid : function(msg)
20998     {
20999         if(this.allowBlank){
21000             return;
21001         }
21002         
21003         var _this = this;
21004         
21005         this.fireEvent('invalid', this, msg);
21006         
21007         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21008         
21009         if(this.groupId){
21010             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21011         }
21012         
21013         if(label){
21014             label.markInvalid();
21015         }
21016             
21017         if(this.inputType == 'radio'){
21018             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21019                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21020                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21021             });
21022             
21023             return;
21024         }
21025         
21026         if(!this.groupId){
21027             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21028             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21029             return;
21030         }
21031         
21032         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21033         
21034         if(!group){
21035             return;
21036         }
21037         
21038         for(var i in group){
21039             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21040             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21041         }
21042         
21043     },
21044     
21045     clearInvalid : function()
21046     {
21047         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21048         
21049         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21050         
21051         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21052         
21053         if (label && label.iconEl) {
21054             label.iconEl.removeClass(label.validClass);
21055             label.iconEl.removeClass(label.invalidClass);
21056         }
21057     },
21058     
21059     disable : function()
21060     {
21061         if(this.inputType != 'radio'){
21062             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21063             return;
21064         }
21065         
21066         var _this = this;
21067         
21068         if(this.rendered){
21069             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21070                 _this.getActionEl().addClass(this.disabledClass);
21071                 e.dom.disabled = true;
21072             });
21073         }
21074         
21075         this.disabled = true;
21076         this.fireEvent("disable", this);
21077         return this;
21078     },
21079
21080     enable : function()
21081     {
21082         if(this.inputType != 'radio'){
21083             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21084             return;
21085         }
21086         
21087         var _this = this;
21088         
21089         if(this.rendered){
21090             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21091                 _this.getActionEl().removeClass(this.disabledClass);
21092                 e.dom.disabled = false;
21093             });
21094         }
21095         
21096         this.disabled = false;
21097         this.fireEvent("enable", this);
21098         return this;
21099     },
21100     
21101     setBoxLabel : function(v)
21102     {
21103         this.boxLabel = v;
21104         
21105         if(this.rendered){
21106             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21107         }
21108     }
21109
21110 });
21111
21112 Roo.apply(Roo.bootstrap.CheckBox, {
21113     
21114     groups: {},
21115     
21116      /**
21117     * register a CheckBox Group
21118     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21119     */
21120     register : function(checkbox)
21121     {
21122         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21123             this.groups[checkbox.groupId] = {};
21124         }
21125         
21126         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21127             return;
21128         }
21129         
21130         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21131         
21132     },
21133     /**
21134     * fetch a CheckBox Group based on the group ID
21135     * @param {string} the group ID
21136     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21137     */
21138     get: function(groupId) {
21139         if (typeof(this.groups[groupId]) == 'undefined') {
21140             return false;
21141         }
21142         
21143         return this.groups[groupId] ;
21144     }
21145     
21146     
21147 });
21148 /*
21149  * - LGPL
21150  *
21151  * RadioItem
21152  * 
21153  */
21154
21155 /**
21156  * @class Roo.bootstrap.Radio
21157  * @extends Roo.bootstrap.Component
21158  * Bootstrap Radio class
21159  * @cfg {String} boxLabel - the label associated
21160  * @cfg {String} value - the value of radio
21161  * 
21162  * @constructor
21163  * Create a new Radio
21164  * @param {Object} config The config object
21165  */
21166 Roo.bootstrap.Radio = function(config){
21167     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21168     
21169 };
21170
21171 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21172     
21173     boxLabel : '',
21174     
21175     value : '',
21176     
21177     getAutoCreate : function()
21178     {
21179         var cfg = {
21180             tag : 'div',
21181             cls : 'form-group radio',
21182             cn : [
21183                 {
21184                     tag : 'label',
21185                     cls : 'box-label',
21186                     html : this.boxLabel
21187                 }
21188             ]
21189         };
21190         
21191         return cfg;
21192     },
21193     
21194     initEvents : function() 
21195     {
21196         this.parent().register(this);
21197         
21198         this.el.on('click', this.onClick, this);
21199         
21200     },
21201     
21202     onClick : function(e)
21203     {
21204         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21205             this.setChecked(true);
21206         }
21207     },
21208     
21209     setChecked : function(state, suppressEvent)
21210     {
21211         this.parent().setValue(this.value, suppressEvent);
21212         
21213     },
21214     
21215     setBoxLabel : function(v)
21216     {
21217         this.boxLabel = v;
21218         
21219         if(this.rendered){
21220             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21221         }
21222     }
21223     
21224 });
21225  
21226
21227  /*
21228  * - LGPL
21229  *
21230  * Input
21231  * 
21232  */
21233
21234 /**
21235  * @class Roo.bootstrap.SecurePass
21236  * @extends Roo.bootstrap.Input
21237  * Bootstrap SecurePass class
21238  *
21239  * 
21240  * @constructor
21241  * Create a new SecurePass
21242  * @param {Object} config The config object
21243  */
21244  
21245 Roo.bootstrap.SecurePass = function (config) {
21246     // these go here, so the translation tool can replace them..
21247     this.errors = {
21248         PwdEmpty: "Please type a password, and then retype it to confirm.",
21249         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21250         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21251         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21252         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21253         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21254         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21255         TooWeak: "Your password is Too Weak."
21256     },
21257     this.meterLabel = "Password strength:";
21258     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21259     this.meterClass = [
21260         "roo-password-meter-tooweak", 
21261         "roo-password-meter-weak", 
21262         "roo-password-meter-medium", 
21263         "roo-password-meter-strong", 
21264         "roo-password-meter-grey"
21265     ];
21266     
21267     this.errors = {};
21268     
21269     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21270 }
21271
21272 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21273     /**
21274      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21275      * {
21276      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21277      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21278      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21279      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21280      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21281      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21282      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21283      * })
21284      */
21285     // private
21286     
21287     meterWidth: 300,
21288     errorMsg :'',    
21289     errors: false,
21290     imageRoot: '/',
21291     /**
21292      * @cfg {String/Object} Label for the strength meter (defaults to
21293      * 'Password strength:')
21294      */
21295     // private
21296     meterLabel: '',
21297     /**
21298      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21299      * ['Weak', 'Medium', 'Strong'])
21300      */
21301     // private    
21302     pwdStrengths: false,    
21303     // private
21304     strength: 0,
21305     // private
21306     _lastPwd: null,
21307     // private
21308     kCapitalLetter: 0,
21309     kSmallLetter: 1,
21310     kDigit: 2,
21311     kPunctuation: 3,
21312     
21313     insecure: false,
21314     // private
21315     initEvents: function ()
21316     {
21317         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21318
21319         if (this.el.is('input[type=password]') && Roo.isSafari) {
21320             this.el.on('keydown', this.SafariOnKeyDown, this);
21321         }
21322
21323         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21324     },
21325     // private
21326     onRender: function (ct, position)
21327     {
21328         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21329         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21330         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21331
21332         this.trigger.createChild({
21333                    cn: [
21334                     {
21335                     //id: 'PwdMeter',
21336                     tag: 'div',
21337                     cls: 'roo-password-meter-grey col-xs-12',
21338                     style: {
21339                         //width: 0,
21340                         //width: this.meterWidth + 'px'                                                
21341                         }
21342                     },
21343                     {                            
21344                          cls: 'roo-password-meter-text'                          
21345                     }
21346                 ]            
21347         });
21348
21349          
21350         if (this.hideTrigger) {
21351             this.trigger.setDisplayed(false);
21352         }
21353         this.setSize(this.width || '', this.height || '');
21354     },
21355     // private
21356     onDestroy: function ()
21357     {
21358         if (this.trigger) {
21359             this.trigger.removeAllListeners();
21360             this.trigger.remove();
21361         }
21362         if (this.wrap) {
21363             this.wrap.remove();
21364         }
21365         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21366     },
21367     // private
21368     checkStrength: function ()
21369     {
21370         var pwd = this.inputEl().getValue();
21371         if (pwd == this._lastPwd) {
21372             return;
21373         }
21374
21375         var strength;
21376         if (this.ClientSideStrongPassword(pwd)) {
21377             strength = 3;
21378         } else if (this.ClientSideMediumPassword(pwd)) {
21379             strength = 2;
21380         } else if (this.ClientSideWeakPassword(pwd)) {
21381             strength = 1;
21382         } else {
21383             strength = 0;
21384         }
21385         
21386         Roo.log('strength1: ' + strength);
21387         
21388         //var pm = this.trigger.child('div/div/div').dom;
21389         var pm = this.trigger.child('div/div');
21390         pm.removeClass(this.meterClass);
21391         pm.addClass(this.meterClass[strength]);
21392                 
21393         
21394         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21395                 
21396         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21397         
21398         this._lastPwd = pwd;
21399     },
21400     reset: function ()
21401     {
21402         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21403         
21404         this._lastPwd = '';
21405         
21406         var pm = this.trigger.child('div/div');
21407         pm.removeClass(this.meterClass);
21408         pm.addClass('roo-password-meter-grey');        
21409         
21410         
21411         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21412         
21413         pt.innerHTML = '';
21414         this.inputEl().dom.type='password';
21415     },
21416     // private
21417     validateValue: function (value)
21418     {
21419         
21420         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21421             return false;
21422         }
21423         if (value.length == 0) {
21424             if (this.allowBlank) {
21425                 this.clearInvalid();
21426                 return true;
21427             }
21428
21429             this.markInvalid(this.errors.PwdEmpty);
21430             this.errorMsg = this.errors.PwdEmpty;
21431             return false;
21432         }
21433         
21434         if(this.insecure){
21435             return true;
21436         }
21437         
21438         if ('[\x21-\x7e]*'.match(value)) {
21439             this.markInvalid(this.errors.PwdBadChar);
21440             this.errorMsg = this.errors.PwdBadChar;
21441             return false;
21442         }
21443         if (value.length < 6) {
21444             this.markInvalid(this.errors.PwdShort);
21445             this.errorMsg = this.errors.PwdShort;
21446             return false;
21447         }
21448         if (value.length > 16) {
21449             this.markInvalid(this.errors.PwdLong);
21450             this.errorMsg = this.errors.PwdLong;
21451             return false;
21452         }
21453         var strength;
21454         if (this.ClientSideStrongPassword(value)) {
21455             strength = 3;
21456         } else if (this.ClientSideMediumPassword(value)) {
21457             strength = 2;
21458         } else if (this.ClientSideWeakPassword(value)) {
21459             strength = 1;
21460         } else {
21461             strength = 0;
21462         }
21463
21464         
21465         if (strength < 2) {
21466             //this.markInvalid(this.errors.TooWeak);
21467             this.errorMsg = this.errors.TooWeak;
21468             //return false;
21469         }
21470         
21471         
21472         console.log('strength2: ' + strength);
21473         
21474         //var pm = this.trigger.child('div/div/div').dom;
21475         
21476         var pm = this.trigger.child('div/div');
21477         pm.removeClass(this.meterClass);
21478         pm.addClass(this.meterClass[strength]);
21479                 
21480         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21481                 
21482         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21483         
21484         this.errorMsg = ''; 
21485         return true;
21486     },
21487     // private
21488     CharacterSetChecks: function (type)
21489     {
21490         this.type = type;
21491         this.fResult = false;
21492     },
21493     // private
21494     isctype: function (character, type)
21495     {
21496         switch (type) {  
21497             case this.kCapitalLetter:
21498                 if (character >= 'A' && character <= 'Z') {
21499                     return true;
21500                 }
21501                 break;
21502             
21503             case this.kSmallLetter:
21504                 if (character >= 'a' && character <= 'z') {
21505                     return true;
21506                 }
21507                 break;
21508             
21509             case this.kDigit:
21510                 if (character >= '0' && character <= '9') {
21511                     return true;
21512                 }
21513                 break;
21514             
21515             case this.kPunctuation:
21516                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21517                     return true;
21518                 }
21519                 break;
21520             
21521             default:
21522                 return false;
21523         }
21524
21525     },
21526     // private
21527     IsLongEnough: function (pwd, size)
21528     {
21529         return !(pwd == null || isNaN(size) || pwd.length < size);
21530     },
21531     // private
21532     SpansEnoughCharacterSets: function (word, nb)
21533     {
21534         if (!this.IsLongEnough(word, nb))
21535         {
21536             return false;
21537         }
21538
21539         var characterSetChecks = new Array(
21540             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21541             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21542         );
21543         
21544         for (var index = 0; index < word.length; ++index) {
21545             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21546                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21547                     characterSetChecks[nCharSet].fResult = true;
21548                     break;
21549                 }
21550             }
21551         }
21552
21553         var nCharSets = 0;
21554         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21555             if (characterSetChecks[nCharSet].fResult) {
21556                 ++nCharSets;
21557             }
21558         }
21559
21560         if (nCharSets < nb) {
21561             return false;
21562         }
21563         return true;
21564     },
21565     // private
21566     ClientSideStrongPassword: function (pwd)
21567     {
21568         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21569     },
21570     // private
21571     ClientSideMediumPassword: function (pwd)
21572     {
21573         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21574     },
21575     // private
21576     ClientSideWeakPassword: function (pwd)
21577     {
21578         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21579     }
21580           
21581 })//<script type="text/javascript">
21582
21583 /*
21584  * Based  Ext JS Library 1.1.1
21585  * Copyright(c) 2006-2007, Ext JS, LLC.
21586  * LGPL
21587  *
21588  */
21589  
21590 /**
21591  * @class Roo.HtmlEditorCore
21592  * @extends Roo.Component
21593  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21594  *
21595  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21596  */
21597
21598 Roo.HtmlEditorCore = function(config){
21599     
21600     
21601     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21602     
21603     
21604     this.addEvents({
21605         /**
21606          * @event initialize
21607          * Fires when the editor is fully initialized (including the iframe)
21608          * @param {Roo.HtmlEditorCore} this
21609          */
21610         initialize: true,
21611         /**
21612          * @event activate
21613          * Fires when the editor is first receives the focus. Any insertion must wait
21614          * until after this event.
21615          * @param {Roo.HtmlEditorCore} this
21616          */
21617         activate: true,
21618          /**
21619          * @event beforesync
21620          * Fires before the textarea is updated with content from the editor iframe. Return false
21621          * to cancel the sync.
21622          * @param {Roo.HtmlEditorCore} this
21623          * @param {String} html
21624          */
21625         beforesync: true,
21626          /**
21627          * @event beforepush
21628          * Fires before the iframe editor is updated with content from the textarea. Return false
21629          * to cancel the push.
21630          * @param {Roo.HtmlEditorCore} this
21631          * @param {String} html
21632          */
21633         beforepush: true,
21634          /**
21635          * @event sync
21636          * Fires when the textarea is updated with content from the editor iframe.
21637          * @param {Roo.HtmlEditorCore} this
21638          * @param {String} html
21639          */
21640         sync: true,
21641          /**
21642          * @event push
21643          * Fires when the iframe editor is updated with content from the textarea.
21644          * @param {Roo.HtmlEditorCore} this
21645          * @param {String} html
21646          */
21647         push: true,
21648         
21649         /**
21650          * @event editorevent
21651          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21652          * @param {Roo.HtmlEditorCore} this
21653          */
21654         editorevent: true
21655         
21656     });
21657     
21658     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21659     
21660     // defaults : white / black...
21661     this.applyBlacklists();
21662     
21663     
21664     
21665 };
21666
21667
21668 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21669
21670
21671      /**
21672      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21673      */
21674     
21675     owner : false,
21676     
21677      /**
21678      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21679      *                        Roo.resizable.
21680      */
21681     resizable : false,
21682      /**
21683      * @cfg {Number} height (in pixels)
21684      */   
21685     height: 300,
21686    /**
21687      * @cfg {Number} width (in pixels)
21688      */   
21689     width: 500,
21690     
21691     /**
21692      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21693      * 
21694      */
21695     stylesheets: false,
21696     
21697     // id of frame..
21698     frameId: false,
21699     
21700     // private properties
21701     validationEvent : false,
21702     deferHeight: true,
21703     initialized : false,
21704     activated : false,
21705     sourceEditMode : false,
21706     onFocus : Roo.emptyFn,
21707     iframePad:3,
21708     hideMode:'offsets',
21709     
21710     clearUp: true,
21711     
21712     // blacklist + whitelisted elements..
21713     black: false,
21714     white: false,
21715      
21716     bodyCls : '',
21717
21718     /**
21719      * Protected method that will not generally be called directly. It
21720      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21721      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21722      */
21723     getDocMarkup : function(){
21724         // body styles..
21725         var st = '';
21726         
21727         // inherit styels from page...?? 
21728         if (this.stylesheets === false) {
21729             
21730             Roo.get(document.head).select('style').each(function(node) {
21731                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21732             });
21733             
21734             Roo.get(document.head).select('link').each(function(node) { 
21735                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21736             });
21737             
21738         } else if (!this.stylesheets.length) {
21739                 // simple..
21740                 st = '<style type="text/css">' +
21741                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21742                    '</style>';
21743         } else { 
21744             st = '<style type="text/css">' +
21745                     this.stylesheets +
21746                 '</style>';
21747         }
21748         
21749         st +=  '<style type="text/css">' +
21750             'IMG { cursor: pointer } ' +
21751         '</style>';
21752
21753         var cls = 'roo-htmleditor-body';
21754         
21755         if(this.bodyCls.length){
21756             cls += ' ' + this.bodyCls;
21757         }
21758         
21759         return '<html><head>' + st  +
21760             //<style type="text/css">' +
21761             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21762             //'</style>' +
21763             ' </head><body class="' +  cls + '"></body></html>';
21764     },
21765
21766     // private
21767     onRender : function(ct, position)
21768     {
21769         var _t = this;
21770         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21771         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21772         
21773         
21774         this.el.dom.style.border = '0 none';
21775         this.el.dom.setAttribute('tabIndex', -1);
21776         this.el.addClass('x-hidden hide');
21777         
21778         
21779         
21780         if(Roo.isIE){ // fix IE 1px bogus margin
21781             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21782         }
21783        
21784         
21785         this.frameId = Roo.id();
21786         
21787          
21788         
21789         var iframe = this.owner.wrap.createChild({
21790             tag: 'iframe',
21791             cls: 'form-control', // bootstrap..
21792             id: this.frameId,
21793             name: this.frameId,
21794             frameBorder : 'no',
21795             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21796         }, this.el
21797         );
21798         
21799         
21800         this.iframe = iframe.dom;
21801
21802          this.assignDocWin();
21803         
21804         this.doc.designMode = 'on';
21805        
21806         this.doc.open();
21807         this.doc.write(this.getDocMarkup());
21808         this.doc.close();
21809
21810         
21811         var task = { // must defer to wait for browser to be ready
21812             run : function(){
21813                 //console.log("run task?" + this.doc.readyState);
21814                 this.assignDocWin();
21815                 if(this.doc.body || this.doc.readyState == 'complete'){
21816                     try {
21817                         this.doc.designMode="on";
21818                     } catch (e) {
21819                         return;
21820                     }
21821                     Roo.TaskMgr.stop(task);
21822                     this.initEditor.defer(10, this);
21823                 }
21824             },
21825             interval : 10,
21826             duration: 10000,
21827             scope: this
21828         };
21829         Roo.TaskMgr.start(task);
21830
21831     },
21832
21833     // private
21834     onResize : function(w, h)
21835     {
21836          Roo.log('resize: ' +w + ',' + h );
21837         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21838         if(!this.iframe){
21839             return;
21840         }
21841         if(typeof w == 'number'){
21842             
21843             this.iframe.style.width = w + 'px';
21844         }
21845         if(typeof h == 'number'){
21846             
21847             this.iframe.style.height = h + 'px';
21848             if(this.doc){
21849                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21850             }
21851         }
21852         
21853     },
21854
21855     /**
21856      * Toggles the editor between standard and source edit mode.
21857      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21858      */
21859     toggleSourceEdit : function(sourceEditMode){
21860         
21861         this.sourceEditMode = sourceEditMode === true;
21862         
21863         if(this.sourceEditMode){
21864  
21865             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21866             
21867         }else{
21868             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21869             //this.iframe.className = '';
21870             this.deferFocus();
21871         }
21872         //this.setSize(this.owner.wrap.getSize());
21873         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21874     },
21875
21876     
21877   
21878
21879     /**
21880      * Protected method that will not generally be called directly. If you need/want
21881      * custom HTML cleanup, this is the method you should override.
21882      * @param {String} html The HTML to be cleaned
21883      * return {String} The cleaned HTML
21884      */
21885     cleanHtml : function(html){
21886         html = String(html);
21887         if(html.length > 5){
21888             if(Roo.isSafari){ // strip safari nonsense
21889                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21890             }
21891         }
21892         if(html == '&nbsp;'){
21893             html = '';
21894         }
21895         return html;
21896     },
21897
21898     /**
21899      * HTML Editor -> Textarea
21900      * Protected method that will not generally be called directly. Syncs the contents
21901      * of the editor iframe with the textarea.
21902      */
21903     syncValue : function(){
21904         if(this.initialized){
21905             var bd = (this.doc.body || this.doc.documentElement);
21906             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21907             var html = bd.innerHTML;
21908             if(Roo.isSafari){
21909                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21910                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21911                 if(m && m[1]){
21912                     html = '<div style="'+m[0]+'">' + html + '</div>';
21913                 }
21914             }
21915             html = this.cleanHtml(html);
21916             // fix up the special chars.. normaly like back quotes in word...
21917             // however we do not want to do this with chinese..
21918             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21919                 var cc = b.charCodeAt();
21920                 if (
21921                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21922                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21923                     (cc >= 0xf900 && cc < 0xfb00 )
21924                 ) {
21925                         return b;
21926                 }
21927                 return "&#"+cc+";" 
21928             });
21929             if(this.owner.fireEvent('beforesync', this, html) !== false){
21930                 this.el.dom.value = html;
21931                 this.owner.fireEvent('sync', this, html);
21932             }
21933         }
21934     },
21935
21936     /**
21937      * Protected method that will not generally be called directly. Pushes the value of the textarea
21938      * into the iframe editor.
21939      */
21940     pushValue : function(){
21941         if(this.initialized){
21942             var v = this.el.dom.value.trim();
21943             
21944 //            if(v.length < 1){
21945 //                v = '&#160;';
21946 //            }
21947             
21948             if(this.owner.fireEvent('beforepush', this, v) !== false){
21949                 var d = (this.doc.body || this.doc.documentElement);
21950                 d.innerHTML = v;
21951                 this.cleanUpPaste();
21952                 this.el.dom.value = d.innerHTML;
21953                 this.owner.fireEvent('push', this, v);
21954             }
21955         }
21956     },
21957
21958     // private
21959     deferFocus : function(){
21960         this.focus.defer(10, this);
21961     },
21962
21963     // doc'ed in Field
21964     focus : function(){
21965         if(this.win && !this.sourceEditMode){
21966             this.win.focus();
21967         }else{
21968             this.el.focus();
21969         }
21970     },
21971     
21972     assignDocWin: function()
21973     {
21974         var iframe = this.iframe;
21975         
21976          if(Roo.isIE){
21977             this.doc = iframe.contentWindow.document;
21978             this.win = iframe.contentWindow;
21979         } else {
21980 //            if (!Roo.get(this.frameId)) {
21981 //                return;
21982 //            }
21983 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21984 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21985             
21986             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21987                 return;
21988             }
21989             
21990             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21991             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21992         }
21993     },
21994     
21995     // private
21996     initEditor : function(){
21997         //console.log("INIT EDITOR");
21998         this.assignDocWin();
21999         
22000         
22001         
22002         this.doc.designMode="on";
22003         this.doc.open();
22004         this.doc.write(this.getDocMarkup());
22005         this.doc.close();
22006         
22007         var dbody = (this.doc.body || this.doc.documentElement);
22008         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22009         // this copies styles from the containing element into thsi one..
22010         // not sure why we need all of this..
22011         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22012         
22013         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22014         //ss['background-attachment'] = 'fixed'; // w3c
22015         dbody.bgProperties = 'fixed'; // ie
22016         //Roo.DomHelper.applyStyles(dbody, ss);
22017         Roo.EventManager.on(this.doc, {
22018             //'mousedown': this.onEditorEvent,
22019             'mouseup': this.onEditorEvent,
22020             'dblclick': this.onEditorEvent,
22021             'click': this.onEditorEvent,
22022             'keyup': this.onEditorEvent,
22023             buffer:100,
22024             scope: this
22025         });
22026         if(Roo.isGecko){
22027             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22028         }
22029         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22030             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22031         }
22032         this.initialized = true;
22033
22034         this.owner.fireEvent('initialize', this);
22035         this.pushValue();
22036     },
22037
22038     // private
22039     onDestroy : function(){
22040         
22041         
22042         
22043         if(this.rendered){
22044             
22045             //for (var i =0; i < this.toolbars.length;i++) {
22046             //    // fixme - ask toolbars for heights?
22047             //    this.toolbars[i].onDestroy();
22048            // }
22049             
22050             //this.wrap.dom.innerHTML = '';
22051             //this.wrap.remove();
22052         }
22053     },
22054
22055     // private
22056     onFirstFocus : function(){
22057         
22058         this.assignDocWin();
22059         
22060         
22061         this.activated = true;
22062          
22063     
22064         if(Roo.isGecko){ // prevent silly gecko errors
22065             this.win.focus();
22066             var s = this.win.getSelection();
22067             if(!s.focusNode || s.focusNode.nodeType != 3){
22068                 var r = s.getRangeAt(0);
22069                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22070                 r.collapse(true);
22071                 this.deferFocus();
22072             }
22073             try{
22074                 this.execCmd('useCSS', true);
22075                 this.execCmd('styleWithCSS', false);
22076             }catch(e){}
22077         }
22078         this.owner.fireEvent('activate', this);
22079     },
22080
22081     // private
22082     adjustFont: function(btn){
22083         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22084         //if(Roo.isSafari){ // safari
22085         //    adjust *= 2;
22086        // }
22087         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22088         if(Roo.isSafari){ // safari
22089             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22090             v =  (v < 10) ? 10 : v;
22091             v =  (v > 48) ? 48 : v;
22092             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22093             
22094         }
22095         
22096         
22097         v = Math.max(1, v+adjust);
22098         
22099         this.execCmd('FontSize', v  );
22100     },
22101
22102     onEditorEvent : function(e)
22103     {
22104         this.owner.fireEvent('editorevent', this, e);
22105       //  this.updateToolbar();
22106         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22107     },
22108
22109     insertTag : function(tg)
22110     {
22111         // could be a bit smarter... -> wrap the current selected tRoo..
22112         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22113             
22114             range = this.createRange(this.getSelection());
22115             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22116             wrappingNode.appendChild(range.extractContents());
22117             range.insertNode(wrappingNode);
22118
22119             return;
22120             
22121             
22122             
22123         }
22124         this.execCmd("formatblock",   tg);
22125         
22126     },
22127     
22128     insertText : function(txt)
22129     {
22130         
22131         
22132         var range = this.createRange();
22133         range.deleteContents();
22134                //alert(Sender.getAttribute('label'));
22135                
22136         range.insertNode(this.doc.createTextNode(txt));
22137     } ,
22138     
22139      
22140
22141     /**
22142      * Executes a Midas editor command on the editor document and performs necessary focus and
22143      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22144      * @param {String} cmd The Midas command
22145      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22146      */
22147     relayCmd : function(cmd, value){
22148         this.win.focus();
22149         this.execCmd(cmd, value);
22150         this.owner.fireEvent('editorevent', this);
22151         //this.updateToolbar();
22152         this.owner.deferFocus();
22153     },
22154
22155     /**
22156      * Executes a Midas editor command directly on the editor document.
22157      * For visual commands, you should use {@link #relayCmd} instead.
22158      * <b>This should only be called after the editor is initialized.</b>
22159      * @param {String} cmd The Midas command
22160      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22161      */
22162     execCmd : function(cmd, value){
22163         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22164         this.syncValue();
22165     },
22166  
22167  
22168    
22169     /**
22170      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22171      * to insert tRoo.
22172      * @param {String} text | dom node.. 
22173      */
22174     insertAtCursor : function(text)
22175     {
22176         
22177         if(!this.activated){
22178             return;
22179         }
22180         /*
22181         if(Roo.isIE){
22182             this.win.focus();
22183             var r = this.doc.selection.createRange();
22184             if(r){
22185                 r.collapse(true);
22186                 r.pasteHTML(text);
22187                 this.syncValue();
22188                 this.deferFocus();
22189             
22190             }
22191             return;
22192         }
22193         */
22194         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22195             this.win.focus();
22196             
22197             
22198             // from jquery ui (MIT licenced)
22199             var range, node;
22200             var win = this.win;
22201             
22202             if (win.getSelection && win.getSelection().getRangeAt) {
22203                 range = win.getSelection().getRangeAt(0);
22204                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22205                 range.insertNode(node);
22206             } else if (win.document.selection && win.document.selection.createRange) {
22207                 // no firefox support
22208                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22209                 win.document.selection.createRange().pasteHTML(txt);
22210             } else {
22211                 // no firefox support
22212                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22213                 this.execCmd('InsertHTML', txt);
22214             } 
22215             
22216             this.syncValue();
22217             
22218             this.deferFocus();
22219         }
22220     },
22221  // private
22222     mozKeyPress : function(e){
22223         if(e.ctrlKey){
22224             var c = e.getCharCode(), cmd;
22225           
22226             if(c > 0){
22227                 c = String.fromCharCode(c).toLowerCase();
22228                 switch(c){
22229                     case 'b':
22230                         cmd = 'bold';
22231                         break;
22232                     case 'i':
22233                         cmd = 'italic';
22234                         break;
22235                     
22236                     case 'u':
22237                         cmd = 'underline';
22238                         break;
22239                     
22240                     case 'v':
22241                         this.cleanUpPaste.defer(100, this);
22242                         return;
22243                         
22244                 }
22245                 if(cmd){
22246                     this.win.focus();
22247                     this.execCmd(cmd);
22248                     this.deferFocus();
22249                     e.preventDefault();
22250                 }
22251                 
22252             }
22253         }
22254     },
22255
22256     // private
22257     fixKeys : function(){ // load time branching for fastest keydown performance
22258         if(Roo.isIE){
22259             return function(e){
22260                 var k = e.getKey(), r;
22261                 if(k == e.TAB){
22262                     e.stopEvent();
22263                     r = this.doc.selection.createRange();
22264                     if(r){
22265                         r.collapse(true);
22266                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22267                         this.deferFocus();
22268                     }
22269                     return;
22270                 }
22271                 
22272                 if(k == e.ENTER){
22273                     r = this.doc.selection.createRange();
22274                     if(r){
22275                         var target = r.parentElement();
22276                         if(!target || target.tagName.toLowerCase() != 'li'){
22277                             e.stopEvent();
22278                             r.pasteHTML('<br />');
22279                             r.collapse(false);
22280                             r.select();
22281                         }
22282                     }
22283                 }
22284                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22285                     this.cleanUpPaste.defer(100, this);
22286                     return;
22287                 }
22288                 
22289                 
22290             };
22291         }else if(Roo.isOpera){
22292             return function(e){
22293                 var k = e.getKey();
22294                 if(k == e.TAB){
22295                     e.stopEvent();
22296                     this.win.focus();
22297                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22298                     this.deferFocus();
22299                 }
22300                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22301                     this.cleanUpPaste.defer(100, this);
22302                     return;
22303                 }
22304                 
22305             };
22306         }else if(Roo.isSafari){
22307             return function(e){
22308                 var k = e.getKey();
22309                 
22310                 if(k == e.TAB){
22311                     e.stopEvent();
22312                     this.execCmd('InsertText','\t');
22313                     this.deferFocus();
22314                     return;
22315                 }
22316                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22317                     this.cleanUpPaste.defer(100, this);
22318                     return;
22319                 }
22320                 
22321              };
22322         }
22323     }(),
22324     
22325     getAllAncestors: function()
22326     {
22327         var p = this.getSelectedNode();
22328         var a = [];
22329         if (!p) {
22330             a.push(p); // push blank onto stack..
22331             p = this.getParentElement();
22332         }
22333         
22334         
22335         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22336             a.push(p);
22337             p = p.parentNode;
22338         }
22339         a.push(this.doc.body);
22340         return a;
22341     },
22342     lastSel : false,
22343     lastSelNode : false,
22344     
22345     
22346     getSelection : function() 
22347     {
22348         this.assignDocWin();
22349         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22350     },
22351     
22352     getSelectedNode: function() 
22353     {
22354         // this may only work on Gecko!!!
22355         
22356         // should we cache this!!!!
22357         
22358         
22359         
22360          
22361         var range = this.createRange(this.getSelection()).cloneRange();
22362         
22363         if (Roo.isIE) {
22364             var parent = range.parentElement();
22365             while (true) {
22366                 var testRange = range.duplicate();
22367                 testRange.moveToElementText(parent);
22368                 if (testRange.inRange(range)) {
22369                     break;
22370                 }
22371                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22372                     break;
22373                 }
22374                 parent = parent.parentElement;
22375             }
22376             return parent;
22377         }
22378         
22379         // is ancestor a text element.
22380         var ac =  range.commonAncestorContainer;
22381         if (ac.nodeType == 3) {
22382             ac = ac.parentNode;
22383         }
22384         
22385         var ar = ac.childNodes;
22386          
22387         var nodes = [];
22388         var other_nodes = [];
22389         var has_other_nodes = false;
22390         for (var i=0;i<ar.length;i++) {
22391             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22392                 continue;
22393             }
22394             // fullly contained node.
22395             
22396             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22397                 nodes.push(ar[i]);
22398                 continue;
22399             }
22400             
22401             // probably selected..
22402             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22403                 other_nodes.push(ar[i]);
22404                 continue;
22405             }
22406             // outer..
22407             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22408                 continue;
22409             }
22410             
22411             
22412             has_other_nodes = true;
22413         }
22414         if (!nodes.length && other_nodes.length) {
22415             nodes= other_nodes;
22416         }
22417         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22418             return false;
22419         }
22420         
22421         return nodes[0];
22422     },
22423     createRange: function(sel)
22424     {
22425         // this has strange effects when using with 
22426         // top toolbar - not sure if it's a great idea.
22427         //this.editor.contentWindow.focus();
22428         if (typeof sel != "undefined") {
22429             try {
22430                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22431             } catch(e) {
22432                 return this.doc.createRange();
22433             }
22434         } else {
22435             return this.doc.createRange();
22436         }
22437     },
22438     getParentElement: function()
22439     {
22440         
22441         this.assignDocWin();
22442         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22443         
22444         var range = this.createRange(sel);
22445          
22446         try {
22447             var p = range.commonAncestorContainer;
22448             while (p.nodeType == 3) { // text node
22449                 p = p.parentNode;
22450             }
22451             return p;
22452         } catch (e) {
22453             return null;
22454         }
22455     
22456     },
22457     /***
22458      *
22459      * Range intersection.. the hard stuff...
22460      *  '-1' = before
22461      *  '0' = hits..
22462      *  '1' = after.
22463      *         [ -- selected range --- ]
22464      *   [fail]                        [fail]
22465      *
22466      *    basically..
22467      *      if end is before start or  hits it. fail.
22468      *      if start is after end or hits it fail.
22469      *
22470      *   if either hits (but other is outside. - then it's not 
22471      *   
22472      *    
22473      **/
22474     
22475     
22476     // @see http://www.thismuchiknow.co.uk/?p=64.
22477     rangeIntersectsNode : function(range, node)
22478     {
22479         var nodeRange = node.ownerDocument.createRange();
22480         try {
22481             nodeRange.selectNode(node);
22482         } catch (e) {
22483             nodeRange.selectNodeContents(node);
22484         }
22485     
22486         var rangeStartRange = range.cloneRange();
22487         rangeStartRange.collapse(true);
22488     
22489         var rangeEndRange = range.cloneRange();
22490         rangeEndRange.collapse(false);
22491     
22492         var nodeStartRange = nodeRange.cloneRange();
22493         nodeStartRange.collapse(true);
22494     
22495         var nodeEndRange = nodeRange.cloneRange();
22496         nodeEndRange.collapse(false);
22497     
22498         return rangeStartRange.compareBoundaryPoints(
22499                  Range.START_TO_START, nodeEndRange) == -1 &&
22500                rangeEndRange.compareBoundaryPoints(
22501                  Range.START_TO_START, nodeStartRange) == 1;
22502         
22503          
22504     },
22505     rangeCompareNode : function(range, node)
22506     {
22507         var nodeRange = node.ownerDocument.createRange();
22508         try {
22509             nodeRange.selectNode(node);
22510         } catch (e) {
22511             nodeRange.selectNodeContents(node);
22512         }
22513         
22514         
22515         range.collapse(true);
22516     
22517         nodeRange.collapse(true);
22518      
22519         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22520         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22521          
22522         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22523         
22524         var nodeIsBefore   =  ss == 1;
22525         var nodeIsAfter    = ee == -1;
22526         
22527         if (nodeIsBefore && nodeIsAfter) {
22528             return 0; // outer
22529         }
22530         if (!nodeIsBefore && nodeIsAfter) {
22531             return 1; //right trailed.
22532         }
22533         
22534         if (nodeIsBefore && !nodeIsAfter) {
22535             return 2;  // left trailed.
22536         }
22537         // fully contined.
22538         return 3;
22539     },
22540
22541     // private? - in a new class?
22542     cleanUpPaste :  function()
22543     {
22544         // cleans up the whole document..
22545         Roo.log('cleanuppaste');
22546         
22547         this.cleanUpChildren(this.doc.body);
22548         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22549         if (clean != this.doc.body.innerHTML) {
22550             this.doc.body.innerHTML = clean;
22551         }
22552         
22553     },
22554     
22555     cleanWordChars : function(input) {// change the chars to hex code
22556         var he = Roo.HtmlEditorCore;
22557         
22558         var output = input;
22559         Roo.each(he.swapCodes, function(sw) { 
22560             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22561             
22562             output = output.replace(swapper, sw[1]);
22563         });
22564         
22565         return output;
22566     },
22567     
22568     
22569     cleanUpChildren : function (n)
22570     {
22571         if (!n.childNodes.length) {
22572             return;
22573         }
22574         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22575            this.cleanUpChild(n.childNodes[i]);
22576         }
22577     },
22578     
22579     
22580         
22581     
22582     cleanUpChild : function (node)
22583     {
22584         var ed = this;
22585         //console.log(node);
22586         if (node.nodeName == "#text") {
22587             // clean up silly Windows -- stuff?
22588             return; 
22589         }
22590         if (node.nodeName == "#comment") {
22591             node.parentNode.removeChild(node);
22592             // clean up silly Windows -- stuff?
22593             return; 
22594         }
22595         var lcname = node.tagName.toLowerCase();
22596         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22597         // whitelist of tags..
22598         
22599         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22600             // remove node.
22601             node.parentNode.removeChild(node);
22602             return;
22603             
22604         }
22605         
22606         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22607         
22608         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22609         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22610         
22611         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22612         //    remove_keep_children = true;
22613         //}
22614         
22615         if (remove_keep_children) {
22616             this.cleanUpChildren(node);
22617             // inserts everything just before this node...
22618             while (node.childNodes.length) {
22619                 var cn = node.childNodes[0];
22620                 node.removeChild(cn);
22621                 node.parentNode.insertBefore(cn, node);
22622             }
22623             node.parentNode.removeChild(node);
22624             return;
22625         }
22626         
22627         if (!node.attributes || !node.attributes.length) {
22628             this.cleanUpChildren(node);
22629             return;
22630         }
22631         
22632         function cleanAttr(n,v)
22633         {
22634             
22635             if (v.match(/^\./) || v.match(/^\//)) {
22636                 return;
22637             }
22638             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22639                 return;
22640             }
22641             if (v.match(/^#/)) {
22642                 return;
22643             }
22644 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22645             node.removeAttribute(n);
22646             
22647         }
22648         
22649         var cwhite = this.cwhite;
22650         var cblack = this.cblack;
22651             
22652         function cleanStyle(n,v)
22653         {
22654             if (v.match(/expression/)) { //XSS?? should we even bother..
22655                 node.removeAttribute(n);
22656                 return;
22657             }
22658             
22659             var parts = v.split(/;/);
22660             var clean = [];
22661             
22662             Roo.each(parts, function(p) {
22663                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22664                 if (!p.length) {
22665                     return true;
22666                 }
22667                 var l = p.split(':').shift().replace(/\s+/g,'');
22668                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22669                 
22670                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22671 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22672                     //node.removeAttribute(n);
22673                     return true;
22674                 }
22675                 //Roo.log()
22676                 // only allow 'c whitelisted system attributes'
22677                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22678 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22679                     //node.removeAttribute(n);
22680                     return true;
22681                 }
22682                 
22683                 
22684                  
22685                 
22686                 clean.push(p);
22687                 return true;
22688             });
22689             if (clean.length) { 
22690                 node.setAttribute(n, clean.join(';'));
22691             } else {
22692                 node.removeAttribute(n);
22693             }
22694             
22695         }
22696         
22697         
22698         for (var i = node.attributes.length-1; i > -1 ; i--) {
22699             var a = node.attributes[i];
22700             //console.log(a);
22701             
22702             if (a.name.toLowerCase().substr(0,2)=='on')  {
22703                 node.removeAttribute(a.name);
22704                 continue;
22705             }
22706             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22707                 node.removeAttribute(a.name);
22708                 continue;
22709             }
22710             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22711                 cleanAttr(a.name,a.value); // fixme..
22712                 continue;
22713             }
22714             if (a.name == 'style') {
22715                 cleanStyle(a.name,a.value);
22716                 continue;
22717             }
22718             /// clean up MS crap..
22719             // tecnically this should be a list of valid class'es..
22720             
22721             
22722             if (a.name == 'class') {
22723                 if (a.value.match(/^Mso/)) {
22724                     node.className = '';
22725                 }
22726                 
22727                 if (a.value.match(/^body$/)) {
22728                     node.className = '';
22729                 }
22730                 continue;
22731             }
22732             
22733             // style cleanup!?
22734             // class cleanup?
22735             
22736         }
22737         
22738         
22739         this.cleanUpChildren(node);
22740         
22741         
22742     },
22743     
22744     /**
22745      * Clean up MS wordisms...
22746      */
22747     cleanWord : function(node)
22748     {
22749         
22750         
22751         if (!node) {
22752             this.cleanWord(this.doc.body);
22753             return;
22754         }
22755         if (node.nodeName == "#text") {
22756             // clean up silly Windows -- stuff?
22757             return; 
22758         }
22759         if (node.nodeName == "#comment") {
22760             node.parentNode.removeChild(node);
22761             // clean up silly Windows -- stuff?
22762             return; 
22763         }
22764         
22765         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22766             node.parentNode.removeChild(node);
22767             return;
22768         }
22769         
22770         // remove - but keep children..
22771         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22772             while (node.childNodes.length) {
22773                 var cn = node.childNodes[0];
22774                 node.removeChild(cn);
22775                 node.parentNode.insertBefore(cn, node);
22776             }
22777             node.parentNode.removeChild(node);
22778             this.iterateChildren(node, this.cleanWord);
22779             return;
22780         }
22781         // clean styles
22782         if (node.className.length) {
22783             
22784             var cn = node.className.split(/\W+/);
22785             var cna = [];
22786             Roo.each(cn, function(cls) {
22787                 if (cls.match(/Mso[a-zA-Z]+/)) {
22788                     return;
22789                 }
22790                 cna.push(cls);
22791             });
22792             node.className = cna.length ? cna.join(' ') : '';
22793             if (!cna.length) {
22794                 node.removeAttribute("class");
22795             }
22796         }
22797         
22798         if (node.hasAttribute("lang")) {
22799             node.removeAttribute("lang");
22800         }
22801         
22802         if (node.hasAttribute("style")) {
22803             
22804             var styles = node.getAttribute("style").split(";");
22805             var nstyle = [];
22806             Roo.each(styles, function(s) {
22807                 if (!s.match(/:/)) {
22808                     return;
22809                 }
22810                 var kv = s.split(":");
22811                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22812                     return;
22813                 }
22814                 // what ever is left... we allow.
22815                 nstyle.push(s);
22816             });
22817             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22818             if (!nstyle.length) {
22819                 node.removeAttribute('style');
22820             }
22821         }
22822         this.iterateChildren(node, this.cleanWord);
22823         
22824         
22825         
22826     },
22827     /**
22828      * iterateChildren of a Node, calling fn each time, using this as the scole..
22829      * @param {DomNode} node node to iterate children of.
22830      * @param {Function} fn method of this class to call on each item.
22831      */
22832     iterateChildren : function(node, fn)
22833     {
22834         if (!node.childNodes.length) {
22835                 return;
22836         }
22837         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22838            fn.call(this, node.childNodes[i])
22839         }
22840     },
22841     
22842     
22843     /**
22844      * cleanTableWidths.
22845      *
22846      * Quite often pasting from word etc.. results in tables with column and widths.
22847      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22848      *
22849      */
22850     cleanTableWidths : function(node)
22851     {
22852          
22853          
22854         if (!node) {
22855             this.cleanTableWidths(this.doc.body);
22856             return;
22857         }
22858         
22859         // ignore list...
22860         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22861             return; 
22862         }
22863         Roo.log(node.tagName);
22864         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22865             this.iterateChildren(node, this.cleanTableWidths);
22866             return;
22867         }
22868         if (node.hasAttribute('width')) {
22869             node.removeAttribute('width');
22870         }
22871         
22872          
22873         if (node.hasAttribute("style")) {
22874             // pretty basic...
22875             
22876             var styles = node.getAttribute("style").split(";");
22877             var nstyle = [];
22878             Roo.each(styles, function(s) {
22879                 if (!s.match(/:/)) {
22880                     return;
22881                 }
22882                 var kv = s.split(":");
22883                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22884                     return;
22885                 }
22886                 // what ever is left... we allow.
22887                 nstyle.push(s);
22888             });
22889             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22890             if (!nstyle.length) {
22891                 node.removeAttribute('style');
22892             }
22893         }
22894         
22895         this.iterateChildren(node, this.cleanTableWidths);
22896         
22897         
22898     },
22899     
22900     
22901     
22902     
22903     domToHTML : function(currentElement, depth, nopadtext) {
22904         
22905         depth = depth || 0;
22906         nopadtext = nopadtext || false;
22907     
22908         if (!currentElement) {
22909             return this.domToHTML(this.doc.body);
22910         }
22911         
22912         //Roo.log(currentElement);
22913         var j;
22914         var allText = false;
22915         var nodeName = currentElement.nodeName;
22916         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22917         
22918         if  (nodeName == '#text') {
22919             
22920             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22921         }
22922         
22923         
22924         var ret = '';
22925         if (nodeName != 'BODY') {
22926              
22927             var i = 0;
22928             // Prints the node tagName, such as <A>, <IMG>, etc
22929             if (tagName) {
22930                 var attr = [];
22931                 for(i = 0; i < currentElement.attributes.length;i++) {
22932                     // quoting?
22933                     var aname = currentElement.attributes.item(i).name;
22934                     if (!currentElement.attributes.item(i).value.length) {
22935                         continue;
22936                     }
22937                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22938                 }
22939                 
22940                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22941             } 
22942             else {
22943                 
22944                 // eack
22945             }
22946         } else {
22947             tagName = false;
22948         }
22949         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22950             return ret;
22951         }
22952         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22953             nopadtext = true;
22954         }
22955         
22956         
22957         // Traverse the tree
22958         i = 0;
22959         var currentElementChild = currentElement.childNodes.item(i);
22960         var allText = true;
22961         var innerHTML  = '';
22962         lastnode = '';
22963         while (currentElementChild) {
22964             // Formatting code (indent the tree so it looks nice on the screen)
22965             var nopad = nopadtext;
22966             if (lastnode == 'SPAN') {
22967                 nopad  = true;
22968             }
22969             // text
22970             if  (currentElementChild.nodeName == '#text') {
22971                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22972                 toadd = nopadtext ? toadd : toadd.trim();
22973                 if (!nopad && toadd.length > 80) {
22974                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22975                 }
22976                 innerHTML  += toadd;
22977                 
22978                 i++;
22979                 currentElementChild = currentElement.childNodes.item(i);
22980                 lastNode = '';
22981                 continue;
22982             }
22983             allText = false;
22984             
22985             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22986                 
22987             // Recursively traverse the tree structure of the child node
22988             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22989             lastnode = currentElementChild.nodeName;
22990             i++;
22991             currentElementChild=currentElement.childNodes.item(i);
22992         }
22993         
22994         ret += innerHTML;
22995         
22996         if (!allText) {
22997                 // The remaining code is mostly for formatting the tree
22998             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22999         }
23000         
23001         
23002         if (tagName) {
23003             ret+= "</"+tagName+">";
23004         }
23005         return ret;
23006         
23007     },
23008         
23009     applyBlacklists : function()
23010     {
23011         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23012         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23013         
23014         this.white = [];
23015         this.black = [];
23016         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23017             if (b.indexOf(tag) > -1) {
23018                 return;
23019             }
23020             this.white.push(tag);
23021             
23022         }, this);
23023         
23024         Roo.each(w, function(tag) {
23025             if (b.indexOf(tag) > -1) {
23026                 return;
23027             }
23028             if (this.white.indexOf(tag) > -1) {
23029                 return;
23030             }
23031             this.white.push(tag);
23032             
23033         }, this);
23034         
23035         
23036         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23037             if (w.indexOf(tag) > -1) {
23038                 return;
23039             }
23040             this.black.push(tag);
23041             
23042         }, this);
23043         
23044         Roo.each(b, function(tag) {
23045             if (w.indexOf(tag) > -1) {
23046                 return;
23047             }
23048             if (this.black.indexOf(tag) > -1) {
23049                 return;
23050             }
23051             this.black.push(tag);
23052             
23053         }, this);
23054         
23055         
23056         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23057         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23058         
23059         this.cwhite = [];
23060         this.cblack = [];
23061         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23062             if (b.indexOf(tag) > -1) {
23063                 return;
23064             }
23065             this.cwhite.push(tag);
23066             
23067         }, this);
23068         
23069         Roo.each(w, function(tag) {
23070             if (b.indexOf(tag) > -1) {
23071                 return;
23072             }
23073             if (this.cwhite.indexOf(tag) > -1) {
23074                 return;
23075             }
23076             this.cwhite.push(tag);
23077             
23078         }, this);
23079         
23080         
23081         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23082             if (w.indexOf(tag) > -1) {
23083                 return;
23084             }
23085             this.cblack.push(tag);
23086             
23087         }, this);
23088         
23089         Roo.each(b, function(tag) {
23090             if (w.indexOf(tag) > -1) {
23091                 return;
23092             }
23093             if (this.cblack.indexOf(tag) > -1) {
23094                 return;
23095             }
23096             this.cblack.push(tag);
23097             
23098         }, this);
23099     },
23100     
23101     setStylesheets : function(stylesheets)
23102     {
23103         if(typeof(stylesheets) == 'string'){
23104             Roo.get(this.iframe.contentDocument.head).createChild({
23105                 tag : 'link',
23106                 rel : 'stylesheet',
23107                 type : 'text/css',
23108                 href : stylesheets
23109             });
23110             
23111             return;
23112         }
23113         var _this = this;
23114      
23115         Roo.each(stylesheets, function(s) {
23116             if(!s.length){
23117                 return;
23118             }
23119             
23120             Roo.get(_this.iframe.contentDocument.head).createChild({
23121                 tag : 'link',
23122                 rel : 'stylesheet',
23123                 type : 'text/css',
23124                 href : s
23125             });
23126         });
23127
23128         
23129     },
23130     
23131     removeStylesheets : function()
23132     {
23133         var _this = this;
23134         
23135         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23136             s.remove();
23137         });
23138     },
23139     
23140     setStyle : function(style)
23141     {
23142         Roo.get(this.iframe.contentDocument.head).createChild({
23143             tag : 'style',
23144             type : 'text/css',
23145             html : style
23146         });
23147
23148         return;
23149     }
23150     
23151     // hide stuff that is not compatible
23152     /**
23153      * @event blur
23154      * @hide
23155      */
23156     /**
23157      * @event change
23158      * @hide
23159      */
23160     /**
23161      * @event focus
23162      * @hide
23163      */
23164     /**
23165      * @event specialkey
23166      * @hide
23167      */
23168     /**
23169      * @cfg {String} fieldClass @hide
23170      */
23171     /**
23172      * @cfg {String} focusClass @hide
23173      */
23174     /**
23175      * @cfg {String} autoCreate @hide
23176      */
23177     /**
23178      * @cfg {String} inputType @hide
23179      */
23180     /**
23181      * @cfg {String} invalidClass @hide
23182      */
23183     /**
23184      * @cfg {String} invalidText @hide
23185      */
23186     /**
23187      * @cfg {String} msgFx @hide
23188      */
23189     /**
23190      * @cfg {String} validateOnBlur @hide
23191      */
23192 });
23193
23194 Roo.HtmlEditorCore.white = [
23195         'area', 'br', 'img', 'input', 'hr', 'wbr',
23196         
23197        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23198        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23199        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23200        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23201        'table',   'ul',         'xmp', 
23202        
23203        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23204       'thead',   'tr', 
23205      
23206       'dir', 'menu', 'ol', 'ul', 'dl',
23207        
23208       'embed',  'object'
23209 ];
23210
23211
23212 Roo.HtmlEditorCore.black = [
23213     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23214         'applet', // 
23215         'base',   'basefont', 'bgsound', 'blink',  'body', 
23216         'frame',  'frameset', 'head',    'html',   'ilayer', 
23217         'iframe', 'layer',  'link',     'meta',    'object',   
23218         'script', 'style' ,'title',  'xml' // clean later..
23219 ];
23220 Roo.HtmlEditorCore.clean = [
23221     'script', 'style', 'title', 'xml'
23222 ];
23223 Roo.HtmlEditorCore.remove = [
23224     'font'
23225 ];
23226 // attributes..
23227
23228 Roo.HtmlEditorCore.ablack = [
23229     'on'
23230 ];
23231     
23232 Roo.HtmlEditorCore.aclean = [ 
23233     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23234 ];
23235
23236 // protocols..
23237 Roo.HtmlEditorCore.pwhite= [
23238         'http',  'https',  'mailto'
23239 ];
23240
23241 // white listed style attributes.
23242 Roo.HtmlEditorCore.cwhite= [
23243       //  'text-align', /// default is to allow most things..
23244       
23245          
23246 //        'font-size'//??
23247 ];
23248
23249 // black listed style attributes.
23250 Roo.HtmlEditorCore.cblack= [
23251       //  'font-size' -- this can be set by the project 
23252 ];
23253
23254
23255 Roo.HtmlEditorCore.swapCodes   =[ 
23256     [    8211, "--" ], 
23257     [    8212, "--" ], 
23258     [    8216,  "'" ],  
23259     [    8217, "'" ],  
23260     [    8220, '"' ],  
23261     [    8221, '"' ],  
23262     [    8226, "*" ],  
23263     [    8230, "..." ]
23264 ]; 
23265
23266     /*
23267  * - LGPL
23268  *
23269  * HtmlEditor
23270  * 
23271  */
23272
23273 /**
23274  * @class Roo.bootstrap.HtmlEditor
23275  * @extends Roo.bootstrap.TextArea
23276  * Bootstrap HtmlEditor class
23277
23278  * @constructor
23279  * Create a new HtmlEditor
23280  * @param {Object} config The config object
23281  */
23282
23283 Roo.bootstrap.HtmlEditor = function(config){
23284     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23285     if (!this.toolbars) {
23286         this.toolbars = [];
23287     }
23288     
23289     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23290     this.addEvents({
23291             /**
23292              * @event initialize
23293              * Fires when the editor is fully initialized (including the iframe)
23294              * @param {HtmlEditor} this
23295              */
23296             initialize: true,
23297             /**
23298              * @event activate
23299              * Fires when the editor is first receives the focus. Any insertion must wait
23300              * until after this event.
23301              * @param {HtmlEditor} this
23302              */
23303             activate: true,
23304              /**
23305              * @event beforesync
23306              * Fires before the textarea is updated with content from the editor iframe. Return false
23307              * to cancel the sync.
23308              * @param {HtmlEditor} this
23309              * @param {String} html
23310              */
23311             beforesync: true,
23312              /**
23313              * @event beforepush
23314              * Fires before the iframe editor is updated with content from the textarea. Return false
23315              * to cancel the push.
23316              * @param {HtmlEditor} this
23317              * @param {String} html
23318              */
23319             beforepush: true,
23320              /**
23321              * @event sync
23322              * Fires when the textarea is updated with content from the editor iframe.
23323              * @param {HtmlEditor} this
23324              * @param {String} html
23325              */
23326             sync: true,
23327              /**
23328              * @event push
23329              * Fires when the iframe editor is updated with content from the textarea.
23330              * @param {HtmlEditor} this
23331              * @param {String} html
23332              */
23333             push: true,
23334              /**
23335              * @event editmodechange
23336              * Fires when the editor switches edit modes
23337              * @param {HtmlEditor} this
23338              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23339              */
23340             editmodechange: true,
23341             /**
23342              * @event editorevent
23343              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23344              * @param {HtmlEditor} this
23345              */
23346             editorevent: true,
23347             /**
23348              * @event firstfocus
23349              * Fires when on first focus - needed by toolbars..
23350              * @param {HtmlEditor} this
23351              */
23352             firstfocus: true,
23353             /**
23354              * @event autosave
23355              * Auto save the htmlEditor value as a file into Events
23356              * @param {HtmlEditor} this
23357              */
23358             autosave: true,
23359             /**
23360              * @event savedpreview
23361              * preview the saved version of htmlEditor
23362              * @param {HtmlEditor} this
23363              */
23364             savedpreview: true
23365         });
23366 };
23367
23368
23369 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23370     
23371     
23372       /**
23373      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23374      */
23375     toolbars : false,
23376     
23377      /**
23378     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23379     */
23380     btns : [],
23381    
23382      /**
23383      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23384      *                        Roo.resizable.
23385      */
23386     resizable : false,
23387      /**
23388      * @cfg {Number} height (in pixels)
23389      */   
23390     height: 300,
23391    /**
23392      * @cfg {Number} width (in pixels)
23393      */   
23394     width: false,
23395     
23396     /**
23397      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23398      * 
23399      */
23400     stylesheets: false,
23401     
23402     // id of frame..
23403     frameId: false,
23404     
23405     // private properties
23406     validationEvent : false,
23407     deferHeight: true,
23408     initialized : false,
23409     activated : false,
23410     
23411     onFocus : Roo.emptyFn,
23412     iframePad:3,
23413     hideMode:'offsets',
23414     
23415     tbContainer : false,
23416     
23417     bodyCls : '',
23418     
23419     toolbarContainer :function() {
23420         return this.wrap.select('.x-html-editor-tb',true).first();
23421     },
23422
23423     /**
23424      * Protected method that will not generally be called directly. It
23425      * is called when the editor creates its toolbar. Override this method if you need to
23426      * add custom toolbar buttons.
23427      * @param {HtmlEditor} editor
23428      */
23429     createToolbar : function(){
23430         Roo.log('renewing');
23431         Roo.log("create toolbars");
23432         
23433         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23434         this.toolbars[0].render(this.toolbarContainer());
23435         
23436         return;
23437         
23438 //        if (!editor.toolbars || !editor.toolbars.length) {
23439 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23440 //        }
23441 //        
23442 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23443 //            editor.toolbars[i] = Roo.factory(
23444 //                    typeof(editor.toolbars[i]) == 'string' ?
23445 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23446 //                Roo.bootstrap.HtmlEditor);
23447 //            editor.toolbars[i].init(editor);
23448 //        }
23449     },
23450
23451      
23452     // private
23453     onRender : function(ct, position)
23454     {
23455        // Roo.log("Call onRender: " + this.xtype);
23456         var _t = this;
23457         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23458       
23459         this.wrap = this.inputEl().wrap({
23460             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23461         });
23462         
23463         this.editorcore.onRender(ct, position);
23464          
23465         if (this.resizable) {
23466             this.resizeEl = new Roo.Resizable(this.wrap, {
23467                 pinned : true,
23468                 wrap: true,
23469                 dynamic : true,
23470                 minHeight : this.height,
23471                 height: this.height,
23472                 handles : this.resizable,
23473                 width: this.width,
23474                 listeners : {
23475                     resize : function(r, w, h) {
23476                         _t.onResize(w,h); // -something
23477                     }
23478                 }
23479             });
23480             
23481         }
23482         this.createToolbar(this);
23483        
23484         
23485         if(!this.width && this.resizable){
23486             this.setSize(this.wrap.getSize());
23487         }
23488         if (this.resizeEl) {
23489             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23490             // should trigger onReize..
23491         }
23492         
23493     },
23494
23495     // private
23496     onResize : function(w, h)
23497     {
23498         Roo.log('resize: ' +w + ',' + h );
23499         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23500         var ew = false;
23501         var eh = false;
23502         
23503         if(this.inputEl() ){
23504             if(typeof w == 'number'){
23505                 var aw = w - this.wrap.getFrameWidth('lr');
23506                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23507                 ew = aw;
23508             }
23509             if(typeof h == 'number'){
23510                  var tbh = -11;  // fixme it needs to tool bar size!
23511                 for (var i =0; i < this.toolbars.length;i++) {
23512                     // fixme - ask toolbars for heights?
23513                     tbh += this.toolbars[i].el.getHeight();
23514                     //if (this.toolbars[i].footer) {
23515                     //    tbh += this.toolbars[i].footer.el.getHeight();
23516                     //}
23517                 }
23518               
23519                 
23520                 
23521                 
23522                 
23523                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23524                 ah -= 5; // knock a few pixes off for look..
23525                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23526                 var eh = ah;
23527             }
23528         }
23529         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23530         this.editorcore.onResize(ew,eh);
23531         
23532     },
23533
23534     /**
23535      * Toggles the editor between standard and source edit mode.
23536      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23537      */
23538     toggleSourceEdit : function(sourceEditMode)
23539     {
23540         this.editorcore.toggleSourceEdit(sourceEditMode);
23541         
23542         if(this.editorcore.sourceEditMode){
23543             Roo.log('editor - showing textarea');
23544             
23545 //            Roo.log('in');
23546 //            Roo.log(this.syncValue());
23547             this.syncValue();
23548             this.inputEl().removeClass(['hide', 'x-hidden']);
23549             this.inputEl().dom.removeAttribute('tabIndex');
23550             this.inputEl().focus();
23551         }else{
23552             Roo.log('editor - hiding textarea');
23553 //            Roo.log('out')
23554 //            Roo.log(this.pushValue()); 
23555             this.pushValue();
23556             
23557             this.inputEl().addClass(['hide', 'x-hidden']);
23558             this.inputEl().dom.setAttribute('tabIndex', -1);
23559             //this.deferFocus();
23560         }
23561          
23562         if(this.resizable){
23563             this.setSize(this.wrap.getSize());
23564         }
23565         
23566         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23567     },
23568  
23569     // private (for BoxComponent)
23570     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23571
23572     // private (for BoxComponent)
23573     getResizeEl : function(){
23574         return this.wrap;
23575     },
23576
23577     // private (for BoxComponent)
23578     getPositionEl : function(){
23579         return this.wrap;
23580     },
23581
23582     // private
23583     initEvents : function(){
23584         this.originalValue = this.getValue();
23585     },
23586
23587 //    /**
23588 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23589 //     * @method
23590 //     */
23591 //    markInvalid : Roo.emptyFn,
23592 //    /**
23593 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23594 //     * @method
23595 //     */
23596 //    clearInvalid : Roo.emptyFn,
23597
23598     setValue : function(v){
23599         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23600         this.editorcore.pushValue();
23601     },
23602
23603      
23604     // private
23605     deferFocus : function(){
23606         this.focus.defer(10, this);
23607     },
23608
23609     // doc'ed in Field
23610     focus : function(){
23611         this.editorcore.focus();
23612         
23613     },
23614       
23615
23616     // private
23617     onDestroy : function(){
23618         
23619         
23620         
23621         if(this.rendered){
23622             
23623             for (var i =0; i < this.toolbars.length;i++) {
23624                 // fixme - ask toolbars for heights?
23625                 this.toolbars[i].onDestroy();
23626             }
23627             
23628             this.wrap.dom.innerHTML = '';
23629             this.wrap.remove();
23630         }
23631     },
23632
23633     // private
23634     onFirstFocus : function(){
23635         //Roo.log("onFirstFocus");
23636         this.editorcore.onFirstFocus();
23637          for (var i =0; i < this.toolbars.length;i++) {
23638             this.toolbars[i].onFirstFocus();
23639         }
23640         
23641     },
23642     
23643     // private
23644     syncValue : function()
23645     {   
23646         this.editorcore.syncValue();
23647     },
23648     
23649     pushValue : function()
23650     {   
23651         this.editorcore.pushValue();
23652     }
23653      
23654     
23655     // hide stuff that is not compatible
23656     /**
23657      * @event blur
23658      * @hide
23659      */
23660     /**
23661      * @event change
23662      * @hide
23663      */
23664     /**
23665      * @event focus
23666      * @hide
23667      */
23668     /**
23669      * @event specialkey
23670      * @hide
23671      */
23672     /**
23673      * @cfg {String} fieldClass @hide
23674      */
23675     /**
23676      * @cfg {String} focusClass @hide
23677      */
23678     /**
23679      * @cfg {String} autoCreate @hide
23680      */
23681     /**
23682      * @cfg {String} inputType @hide
23683      */
23684     /**
23685      * @cfg {String} invalidClass @hide
23686      */
23687     /**
23688      * @cfg {String} invalidText @hide
23689      */
23690     /**
23691      * @cfg {String} msgFx @hide
23692      */
23693     /**
23694      * @cfg {String} validateOnBlur @hide
23695      */
23696 });
23697  
23698     
23699    
23700    
23701    
23702       
23703 Roo.namespace('Roo.bootstrap.htmleditor');
23704 /**
23705  * @class Roo.bootstrap.HtmlEditorToolbar1
23706  * Basic Toolbar
23707  * 
23708  * Usage:
23709  *
23710  new Roo.bootstrap.HtmlEditor({
23711     ....
23712     toolbars : [
23713         new Roo.bootstrap.HtmlEditorToolbar1({
23714             disable : { fonts: 1 , format: 1, ..., ... , ...],
23715             btns : [ .... ]
23716         })
23717     }
23718      
23719  * 
23720  * @cfg {Object} disable List of elements to disable..
23721  * @cfg {Array} btns List of additional buttons.
23722  * 
23723  * 
23724  * NEEDS Extra CSS? 
23725  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23726  */
23727  
23728 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23729 {
23730     
23731     Roo.apply(this, config);
23732     
23733     // default disabled, based on 'good practice'..
23734     this.disable = this.disable || {};
23735     Roo.applyIf(this.disable, {
23736         fontSize : true,
23737         colors : true,
23738         specialElements : true
23739     });
23740     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23741     
23742     this.editor = config.editor;
23743     this.editorcore = config.editor.editorcore;
23744     
23745     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23746     
23747     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23748     // dont call parent... till later.
23749 }
23750 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23751      
23752     bar : true,
23753     
23754     editor : false,
23755     editorcore : false,
23756     
23757     
23758     formats : [
23759         "p" ,  
23760         "h1","h2","h3","h4","h5","h6", 
23761         "pre", "code", 
23762         "abbr", "acronym", "address", "cite", "samp", "var",
23763         'div','span'
23764     ],
23765     
23766     onRender : function(ct, position)
23767     {
23768        // Roo.log("Call onRender: " + this.xtype);
23769         
23770        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23771        Roo.log(this.el);
23772        this.el.dom.style.marginBottom = '0';
23773        var _this = this;
23774        var editorcore = this.editorcore;
23775        var editor= this.editor;
23776        
23777        var children = [];
23778        var btn = function(id,cmd , toggle, handler, html){
23779        
23780             var  event = toggle ? 'toggle' : 'click';
23781        
23782             var a = {
23783                 size : 'sm',
23784                 xtype: 'Button',
23785                 xns: Roo.bootstrap,
23786                 glyphicon : id,
23787                 cmd : id || cmd,
23788                 enableToggle:toggle !== false,
23789                 html : html || '',
23790                 pressed : toggle ? false : null,
23791                 listeners : {}
23792             };
23793             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23794                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23795             };
23796             children.push(a);
23797             return a;
23798        }
23799        
23800     //    var cb_box = function...
23801         
23802         var style = {
23803                 xtype: 'Button',
23804                 size : 'sm',
23805                 xns: Roo.bootstrap,
23806                 glyphicon : 'font',
23807                 //html : 'submit'
23808                 menu : {
23809                     xtype: 'Menu',
23810                     xns: Roo.bootstrap,
23811                     items:  []
23812                 }
23813         };
23814         Roo.each(this.formats, function(f) {
23815             style.menu.items.push({
23816                 xtype :'MenuItem',
23817                 xns: Roo.bootstrap,
23818                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23819                 tagname : f,
23820                 listeners : {
23821                     click : function()
23822                     {
23823                         editorcore.insertTag(this.tagname);
23824                         editor.focus();
23825                     }
23826                 }
23827                 
23828             });
23829         });
23830         children.push(style);   
23831         
23832         btn('bold',false,true);
23833         btn('italic',false,true);
23834         btn('align-left', 'justifyleft',true);
23835         btn('align-center', 'justifycenter',true);
23836         btn('align-right' , 'justifyright',true);
23837         btn('link', false, false, function(btn) {
23838             //Roo.log("create link?");
23839             var url = prompt(this.createLinkText, this.defaultLinkValue);
23840             if(url && url != 'http:/'+'/'){
23841                 this.editorcore.relayCmd('createlink', url);
23842             }
23843         }),
23844         btn('list','insertunorderedlist',true);
23845         btn('pencil', false,true, function(btn){
23846                 Roo.log(this);
23847                 this.toggleSourceEdit(btn.pressed);
23848         });
23849         
23850         if (this.editor.btns.length > 0) {
23851             for (var i = 0; i<this.editor.btns.length; i++) {
23852                 children.push(this.editor.btns[i]);
23853             }
23854         }
23855         
23856         /*
23857         var cog = {
23858                 xtype: 'Button',
23859                 size : 'sm',
23860                 xns: Roo.bootstrap,
23861                 glyphicon : 'cog',
23862                 //html : 'submit'
23863                 menu : {
23864                     xtype: 'Menu',
23865                     xns: Roo.bootstrap,
23866                     items:  []
23867                 }
23868         };
23869         
23870         cog.menu.items.push({
23871             xtype :'MenuItem',
23872             xns: Roo.bootstrap,
23873             html : Clean styles,
23874             tagname : f,
23875             listeners : {
23876                 click : function()
23877                 {
23878                     editorcore.insertTag(this.tagname);
23879                     editor.focus();
23880                 }
23881             }
23882             
23883         });
23884        */
23885         
23886          
23887        this.xtype = 'NavSimplebar';
23888         
23889         for(var i=0;i< children.length;i++) {
23890             
23891             this.buttons.add(this.addxtypeChild(children[i]));
23892             
23893         }
23894         
23895         editor.on('editorevent', this.updateToolbar, this);
23896     },
23897     onBtnClick : function(id)
23898     {
23899        this.editorcore.relayCmd(id);
23900        this.editorcore.focus();
23901     },
23902     
23903     /**
23904      * Protected method that will not generally be called directly. It triggers
23905      * a toolbar update by reading the markup state of the current selection in the editor.
23906      */
23907     updateToolbar: function(){
23908
23909         if(!this.editorcore.activated){
23910             this.editor.onFirstFocus(); // is this neeed?
23911             return;
23912         }
23913
23914         var btns = this.buttons; 
23915         var doc = this.editorcore.doc;
23916         btns.get('bold').setActive(doc.queryCommandState('bold'));
23917         btns.get('italic').setActive(doc.queryCommandState('italic'));
23918         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23919         
23920         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23921         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23922         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23923         
23924         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23925         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23926          /*
23927         
23928         var ans = this.editorcore.getAllAncestors();
23929         if (this.formatCombo) {
23930             
23931             
23932             var store = this.formatCombo.store;
23933             this.formatCombo.setValue("");
23934             for (var i =0; i < ans.length;i++) {
23935                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23936                     // select it..
23937                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23938                     break;
23939                 }
23940             }
23941         }
23942         
23943         
23944         
23945         // hides menus... - so this cant be on a menu...
23946         Roo.bootstrap.MenuMgr.hideAll();
23947         */
23948         Roo.bootstrap.MenuMgr.hideAll();
23949         //this.editorsyncValue();
23950     },
23951     onFirstFocus: function() {
23952         this.buttons.each(function(item){
23953            item.enable();
23954         });
23955     },
23956     toggleSourceEdit : function(sourceEditMode){
23957         
23958           
23959         if(sourceEditMode){
23960             Roo.log("disabling buttons");
23961            this.buttons.each( function(item){
23962                 if(item.cmd != 'pencil'){
23963                     item.disable();
23964                 }
23965             });
23966           
23967         }else{
23968             Roo.log("enabling buttons");
23969             if(this.editorcore.initialized){
23970                 this.buttons.each( function(item){
23971                     item.enable();
23972                 });
23973             }
23974             
23975         }
23976         Roo.log("calling toggole on editor");
23977         // tell the editor that it's been pressed..
23978         this.editor.toggleSourceEdit(sourceEditMode);
23979        
23980     }
23981 });
23982
23983
23984
23985
23986
23987 /**
23988  * @class Roo.bootstrap.Table.AbstractSelectionModel
23989  * @extends Roo.util.Observable
23990  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23991  * implemented by descendant classes.  This class should not be directly instantiated.
23992  * @constructor
23993  */
23994 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23995     this.locked = false;
23996     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23997 };
23998
23999
24000 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24001     /** @ignore Called by the grid automatically. Do not call directly. */
24002     init : function(grid){
24003         this.grid = grid;
24004         this.initEvents();
24005     },
24006
24007     /**
24008      * Locks the selections.
24009      */
24010     lock : function(){
24011         this.locked = true;
24012     },
24013
24014     /**
24015      * Unlocks the selections.
24016      */
24017     unlock : function(){
24018         this.locked = false;
24019     },
24020
24021     /**
24022      * Returns true if the selections are locked.
24023      * @return {Boolean}
24024      */
24025     isLocked : function(){
24026         return this.locked;
24027     }
24028 });
24029 /**
24030  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24031  * @class Roo.bootstrap.Table.RowSelectionModel
24032  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24033  * It supports multiple selections and keyboard selection/navigation. 
24034  * @constructor
24035  * @param {Object} config
24036  */
24037
24038 Roo.bootstrap.Table.RowSelectionModel = function(config){
24039     Roo.apply(this, config);
24040     this.selections = new Roo.util.MixedCollection(false, function(o){
24041         return o.id;
24042     });
24043
24044     this.last = false;
24045     this.lastActive = false;
24046
24047     this.addEvents({
24048         /**
24049              * @event selectionchange
24050              * Fires when the selection changes
24051              * @param {SelectionModel} this
24052              */
24053             "selectionchange" : true,
24054         /**
24055              * @event afterselectionchange
24056              * Fires after the selection changes (eg. by key press or clicking)
24057              * @param {SelectionModel} this
24058              */
24059             "afterselectionchange" : true,
24060         /**
24061              * @event beforerowselect
24062              * Fires when a row is selected being selected, return false to cancel.
24063              * @param {SelectionModel} this
24064              * @param {Number} rowIndex The selected index
24065              * @param {Boolean} keepExisting False if other selections will be cleared
24066              */
24067             "beforerowselect" : true,
24068         /**
24069              * @event rowselect
24070              * Fires when a row is selected.
24071              * @param {SelectionModel} this
24072              * @param {Number} rowIndex The selected index
24073              * @param {Roo.data.Record} r The record
24074              */
24075             "rowselect" : true,
24076         /**
24077              * @event rowdeselect
24078              * Fires when a row is deselected.
24079              * @param {SelectionModel} this
24080              * @param {Number} rowIndex The selected index
24081              */
24082         "rowdeselect" : true
24083     });
24084     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24085     this.locked = false;
24086  };
24087
24088 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24089     /**
24090      * @cfg {Boolean} singleSelect
24091      * True to allow selection of only one row at a time (defaults to false)
24092      */
24093     singleSelect : false,
24094
24095     // private
24096     initEvents : function()
24097     {
24098
24099         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24100         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24101         //}else{ // allow click to work like normal
24102          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24103         //}
24104         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24105         this.grid.on("rowclick", this.handleMouseDown, this);
24106         
24107         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24108             "up" : function(e){
24109                 if(!e.shiftKey){
24110                     this.selectPrevious(e.shiftKey);
24111                 }else if(this.last !== false && this.lastActive !== false){
24112                     var last = this.last;
24113                     this.selectRange(this.last,  this.lastActive-1);
24114                     this.grid.getView().focusRow(this.lastActive);
24115                     if(last !== false){
24116                         this.last = last;
24117                     }
24118                 }else{
24119                     this.selectFirstRow();
24120                 }
24121                 this.fireEvent("afterselectionchange", this);
24122             },
24123             "down" : function(e){
24124                 if(!e.shiftKey){
24125                     this.selectNext(e.shiftKey);
24126                 }else if(this.last !== false && this.lastActive !== false){
24127                     var last = this.last;
24128                     this.selectRange(this.last,  this.lastActive+1);
24129                     this.grid.getView().focusRow(this.lastActive);
24130                     if(last !== false){
24131                         this.last = last;
24132                     }
24133                 }else{
24134                     this.selectFirstRow();
24135                 }
24136                 this.fireEvent("afterselectionchange", this);
24137             },
24138             scope: this
24139         });
24140         this.grid.store.on('load', function(){
24141             this.selections.clear();
24142         },this);
24143         /*
24144         var view = this.grid.view;
24145         view.on("refresh", this.onRefresh, this);
24146         view.on("rowupdated", this.onRowUpdated, this);
24147         view.on("rowremoved", this.onRemove, this);
24148         */
24149     },
24150
24151     // private
24152     onRefresh : function()
24153     {
24154         var ds = this.grid.store, i, v = this.grid.view;
24155         var s = this.selections;
24156         s.each(function(r){
24157             if((i = ds.indexOfId(r.id)) != -1){
24158                 v.onRowSelect(i);
24159             }else{
24160                 s.remove(r);
24161             }
24162         });
24163     },
24164
24165     // private
24166     onRemove : function(v, index, r){
24167         this.selections.remove(r);
24168     },
24169
24170     // private
24171     onRowUpdated : function(v, index, r){
24172         if(this.isSelected(r)){
24173             v.onRowSelect(index);
24174         }
24175     },
24176
24177     /**
24178      * Select records.
24179      * @param {Array} records The records to select
24180      * @param {Boolean} keepExisting (optional) True to keep existing selections
24181      */
24182     selectRecords : function(records, keepExisting)
24183     {
24184         if(!keepExisting){
24185             this.clearSelections();
24186         }
24187             var ds = this.grid.store;
24188         for(var i = 0, len = records.length; i < len; i++){
24189             this.selectRow(ds.indexOf(records[i]), true);
24190         }
24191     },
24192
24193     /**
24194      * Gets the number of selected rows.
24195      * @return {Number}
24196      */
24197     getCount : function(){
24198         return this.selections.length;
24199     },
24200
24201     /**
24202      * Selects the first row in the grid.
24203      */
24204     selectFirstRow : function(){
24205         this.selectRow(0);
24206     },
24207
24208     /**
24209      * Select the last row.
24210      * @param {Boolean} keepExisting (optional) True to keep existing selections
24211      */
24212     selectLastRow : function(keepExisting){
24213         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24214         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24215     },
24216
24217     /**
24218      * Selects the row immediately following the last selected row.
24219      * @param {Boolean} keepExisting (optional) True to keep existing selections
24220      */
24221     selectNext : function(keepExisting)
24222     {
24223             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24224             this.selectRow(this.last+1, keepExisting);
24225             this.grid.getView().focusRow(this.last);
24226         }
24227     },
24228
24229     /**
24230      * Selects the row that precedes the last selected row.
24231      * @param {Boolean} keepExisting (optional) True to keep existing selections
24232      */
24233     selectPrevious : function(keepExisting){
24234         if(this.last){
24235             this.selectRow(this.last-1, keepExisting);
24236             this.grid.getView().focusRow(this.last);
24237         }
24238     },
24239
24240     /**
24241      * Returns the selected records
24242      * @return {Array} Array of selected records
24243      */
24244     getSelections : function(){
24245         return [].concat(this.selections.items);
24246     },
24247
24248     /**
24249      * Returns the first selected record.
24250      * @return {Record}
24251      */
24252     getSelected : function(){
24253         return this.selections.itemAt(0);
24254     },
24255
24256
24257     /**
24258      * Clears all selections.
24259      */
24260     clearSelections : function(fast)
24261     {
24262         if(this.locked) {
24263             return;
24264         }
24265         if(fast !== true){
24266                 var ds = this.grid.store;
24267             var s = this.selections;
24268             s.each(function(r){
24269                 this.deselectRow(ds.indexOfId(r.id));
24270             }, this);
24271             s.clear();
24272         }else{
24273             this.selections.clear();
24274         }
24275         this.last = false;
24276     },
24277
24278
24279     /**
24280      * Selects all rows.
24281      */
24282     selectAll : function(){
24283         if(this.locked) {
24284             return;
24285         }
24286         this.selections.clear();
24287         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24288             this.selectRow(i, true);
24289         }
24290     },
24291
24292     /**
24293      * Returns True if there is a selection.
24294      * @return {Boolean}
24295      */
24296     hasSelection : function(){
24297         return this.selections.length > 0;
24298     },
24299
24300     /**
24301      * Returns True if the specified row is selected.
24302      * @param {Number/Record} record The record or index of the record to check
24303      * @return {Boolean}
24304      */
24305     isSelected : function(index){
24306             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24307         return (r && this.selections.key(r.id) ? true : false);
24308     },
24309
24310     /**
24311      * Returns True if the specified record id is selected.
24312      * @param {String} id The id of record to check
24313      * @return {Boolean}
24314      */
24315     isIdSelected : function(id){
24316         return (this.selections.key(id) ? true : false);
24317     },
24318
24319
24320     // private
24321     handleMouseDBClick : function(e, t){
24322         
24323     },
24324     // private
24325     handleMouseDown : function(e, t)
24326     {
24327             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24328         if(this.isLocked() || rowIndex < 0 ){
24329             return;
24330         };
24331         if(e.shiftKey && this.last !== false){
24332             var last = this.last;
24333             this.selectRange(last, rowIndex, e.ctrlKey);
24334             this.last = last; // reset the last
24335             t.focus();
24336     
24337         }else{
24338             var isSelected = this.isSelected(rowIndex);
24339             //Roo.log("select row:" + rowIndex);
24340             if(isSelected){
24341                 this.deselectRow(rowIndex);
24342             } else {
24343                         this.selectRow(rowIndex, true);
24344             }
24345     
24346             /*
24347                 if(e.button !== 0 && isSelected){
24348                 alert('rowIndex 2: ' + rowIndex);
24349                     view.focusRow(rowIndex);
24350                 }else if(e.ctrlKey && isSelected){
24351                     this.deselectRow(rowIndex);
24352                 }else if(!isSelected){
24353                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24354                     view.focusRow(rowIndex);
24355                 }
24356             */
24357         }
24358         this.fireEvent("afterselectionchange", this);
24359     },
24360     // private
24361     handleDragableRowClick :  function(grid, rowIndex, e) 
24362     {
24363         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24364             this.selectRow(rowIndex, false);
24365             grid.view.focusRow(rowIndex);
24366              this.fireEvent("afterselectionchange", this);
24367         }
24368     },
24369     
24370     /**
24371      * Selects multiple rows.
24372      * @param {Array} rows Array of the indexes of the row to select
24373      * @param {Boolean} keepExisting (optional) True to keep existing selections
24374      */
24375     selectRows : function(rows, keepExisting){
24376         if(!keepExisting){
24377             this.clearSelections();
24378         }
24379         for(var i = 0, len = rows.length; i < len; i++){
24380             this.selectRow(rows[i], true);
24381         }
24382     },
24383
24384     /**
24385      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24386      * @param {Number} startRow The index of the first row in the range
24387      * @param {Number} endRow The index of the last row in the range
24388      * @param {Boolean} keepExisting (optional) True to retain existing selections
24389      */
24390     selectRange : function(startRow, endRow, keepExisting){
24391         if(this.locked) {
24392             return;
24393         }
24394         if(!keepExisting){
24395             this.clearSelections();
24396         }
24397         if(startRow <= endRow){
24398             for(var i = startRow; i <= endRow; i++){
24399                 this.selectRow(i, true);
24400             }
24401         }else{
24402             for(var i = startRow; i >= endRow; i--){
24403                 this.selectRow(i, true);
24404             }
24405         }
24406     },
24407
24408     /**
24409      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24410      * @param {Number} startRow The index of the first row in the range
24411      * @param {Number} endRow The index of the last row in the range
24412      */
24413     deselectRange : function(startRow, endRow, preventViewNotify){
24414         if(this.locked) {
24415             return;
24416         }
24417         for(var i = startRow; i <= endRow; i++){
24418             this.deselectRow(i, preventViewNotify);
24419         }
24420     },
24421
24422     /**
24423      * Selects a row.
24424      * @param {Number} row The index of the row to select
24425      * @param {Boolean} keepExisting (optional) True to keep existing selections
24426      */
24427     selectRow : function(index, keepExisting, preventViewNotify)
24428     {
24429             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24430             return;
24431         }
24432         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24433             if(!keepExisting || this.singleSelect){
24434                 this.clearSelections();
24435             }
24436             
24437             var r = this.grid.store.getAt(index);
24438             //console.log('selectRow - record id :' + r.id);
24439             
24440             this.selections.add(r);
24441             this.last = this.lastActive = index;
24442             if(!preventViewNotify){
24443                 var proxy = new Roo.Element(
24444                                 this.grid.getRowDom(index)
24445                 );
24446                 proxy.addClass('bg-info info');
24447             }
24448             this.fireEvent("rowselect", this, index, r);
24449             this.fireEvent("selectionchange", this);
24450         }
24451     },
24452
24453     /**
24454      * Deselects a row.
24455      * @param {Number} row The index of the row to deselect
24456      */
24457     deselectRow : function(index, preventViewNotify)
24458     {
24459         if(this.locked) {
24460             return;
24461         }
24462         if(this.last == index){
24463             this.last = false;
24464         }
24465         if(this.lastActive == index){
24466             this.lastActive = false;
24467         }
24468         
24469         var r = this.grid.store.getAt(index);
24470         if (!r) {
24471             return;
24472         }
24473         
24474         this.selections.remove(r);
24475         //.console.log('deselectRow - record id :' + r.id);
24476         if(!preventViewNotify){
24477         
24478             var proxy = new Roo.Element(
24479                 this.grid.getRowDom(index)
24480             );
24481             proxy.removeClass('bg-info info');
24482         }
24483         this.fireEvent("rowdeselect", this, index);
24484         this.fireEvent("selectionchange", this);
24485     },
24486
24487     // private
24488     restoreLast : function(){
24489         if(this._last){
24490             this.last = this._last;
24491         }
24492     },
24493
24494     // private
24495     acceptsNav : function(row, col, cm){
24496         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24497     },
24498
24499     // private
24500     onEditorKey : function(field, e){
24501         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24502         if(k == e.TAB){
24503             e.stopEvent();
24504             ed.completeEdit();
24505             if(e.shiftKey){
24506                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24507             }else{
24508                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24509             }
24510         }else if(k == e.ENTER && !e.ctrlKey){
24511             e.stopEvent();
24512             ed.completeEdit();
24513             if(e.shiftKey){
24514                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24515             }else{
24516                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24517             }
24518         }else if(k == e.ESC){
24519             ed.cancelEdit();
24520         }
24521         if(newCell){
24522             g.startEditing(newCell[0], newCell[1]);
24523         }
24524     }
24525 });
24526 /*
24527  * Based on:
24528  * Ext JS Library 1.1.1
24529  * Copyright(c) 2006-2007, Ext JS, LLC.
24530  *
24531  * Originally Released Under LGPL - original licence link has changed is not relivant.
24532  *
24533  * Fork - LGPL
24534  * <script type="text/javascript">
24535  */
24536  
24537 /**
24538  * @class Roo.bootstrap.PagingToolbar
24539  * @extends Roo.bootstrap.NavSimplebar
24540  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24541  * @constructor
24542  * Create a new PagingToolbar
24543  * @param {Object} config The config object
24544  * @param {Roo.data.Store} store
24545  */
24546 Roo.bootstrap.PagingToolbar = function(config)
24547 {
24548     // old args format still supported... - xtype is prefered..
24549         // created from xtype...
24550     
24551     this.ds = config.dataSource;
24552     
24553     if (config.store && !this.ds) {
24554         this.store= Roo.factory(config.store, Roo.data);
24555         this.ds = this.store;
24556         this.ds.xmodule = this.xmodule || false;
24557     }
24558     
24559     this.toolbarItems = [];
24560     if (config.items) {
24561         this.toolbarItems = config.items;
24562     }
24563     
24564     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24565     
24566     this.cursor = 0;
24567     
24568     if (this.ds) { 
24569         this.bind(this.ds);
24570     }
24571     
24572     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24573     
24574 };
24575
24576 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24577     /**
24578      * @cfg {Roo.data.Store} dataSource
24579      * The underlying data store providing the paged data
24580      */
24581     /**
24582      * @cfg {String/HTMLElement/Element} container
24583      * container The id or element that will contain the toolbar
24584      */
24585     /**
24586      * @cfg {Boolean} displayInfo
24587      * True to display the displayMsg (defaults to false)
24588      */
24589     /**
24590      * @cfg {Number} pageSize
24591      * The number of records to display per page (defaults to 20)
24592      */
24593     pageSize: 20,
24594     /**
24595      * @cfg {String} displayMsg
24596      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24597      */
24598     displayMsg : 'Displaying {0} - {1} of {2}',
24599     /**
24600      * @cfg {String} emptyMsg
24601      * The message to display when no records are found (defaults to "No data to display")
24602      */
24603     emptyMsg : 'No data to display',
24604     /**
24605      * Customizable piece of the default paging text (defaults to "Page")
24606      * @type String
24607      */
24608     beforePageText : "Page",
24609     /**
24610      * Customizable piece of the default paging text (defaults to "of %0")
24611      * @type String
24612      */
24613     afterPageText : "of {0}",
24614     /**
24615      * Customizable piece of the default paging text (defaults to "First Page")
24616      * @type String
24617      */
24618     firstText : "First Page",
24619     /**
24620      * Customizable piece of the default paging text (defaults to "Previous Page")
24621      * @type String
24622      */
24623     prevText : "Previous Page",
24624     /**
24625      * Customizable piece of the default paging text (defaults to "Next Page")
24626      * @type String
24627      */
24628     nextText : "Next Page",
24629     /**
24630      * Customizable piece of the default paging text (defaults to "Last Page")
24631      * @type String
24632      */
24633     lastText : "Last Page",
24634     /**
24635      * Customizable piece of the default paging text (defaults to "Refresh")
24636      * @type String
24637      */
24638     refreshText : "Refresh",
24639
24640     buttons : false,
24641     // private
24642     onRender : function(ct, position) 
24643     {
24644         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24645         this.navgroup.parentId = this.id;
24646         this.navgroup.onRender(this.el, null);
24647         // add the buttons to the navgroup
24648         
24649         if(this.displayInfo){
24650             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24651             this.displayEl = this.el.select('.x-paging-info', true).first();
24652 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24653 //            this.displayEl = navel.el.select('span',true).first();
24654         }
24655         
24656         var _this = this;
24657         
24658         if(this.buttons){
24659             Roo.each(_this.buttons, function(e){ // this might need to use render????
24660                Roo.factory(e).render(_this.el);
24661             });
24662         }
24663             
24664         Roo.each(_this.toolbarItems, function(e) {
24665             _this.navgroup.addItem(e);
24666         });
24667         
24668         
24669         this.first = this.navgroup.addItem({
24670             tooltip: this.firstText,
24671             cls: "prev",
24672             icon : 'fa fa-backward',
24673             disabled: true,
24674             preventDefault: true,
24675             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24676         });
24677         
24678         this.prev =  this.navgroup.addItem({
24679             tooltip: this.prevText,
24680             cls: "prev",
24681             icon : 'fa fa-step-backward',
24682             disabled: true,
24683             preventDefault: true,
24684             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24685         });
24686     //this.addSeparator();
24687         
24688         
24689         var field = this.navgroup.addItem( {
24690             tagtype : 'span',
24691             cls : 'x-paging-position',
24692             
24693             html : this.beforePageText  +
24694                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24695                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24696          } ); //?? escaped?
24697         
24698         this.field = field.el.select('input', true).first();
24699         this.field.on("keydown", this.onPagingKeydown, this);
24700         this.field.on("focus", function(){this.dom.select();});
24701     
24702     
24703         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24704         //this.field.setHeight(18);
24705         //this.addSeparator();
24706         this.next = this.navgroup.addItem({
24707             tooltip: this.nextText,
24708             cls: "next",
24709             html : ' <i class="fa fa-step-forward">',
24710             disabled: true,
24711             preventDefault: true,
24712             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24713         });
24714         this.last = this.navgroup.addItem({
24715             tooltip: this.lastText,
24716             icon : 'fa fa-forward',
24717             cls: "next",
24718             disabled: true,
24719             preventDefault: true,
24720             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24721         });
24722     //this.addSeparator();
24723         this.loading = this.navgroup.addItem({
24724             tooltip: this.refreshText,
24725             icon: 'fa fa-refresh',
24726             preventDefault: true,
24727             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24728         });
24729         
24730     },
24731
24732     // private
24733     updateInfo : function(){
24734         if(this.displayEl){
24735             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24736             var msg = count == 0 ?
24737                 this.emptyMsg :
24738                 String.format(
24739                     this.displayMsg,
24740                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24741                 );
24742             this.displayEl.update(msg);
24743         }
24744     },
24745
24746     // private
24747     onLoad : function(ds, r, o)
24748     {
24749         this.cursor = o.params.start ? o.params.start : 0;
24750         
24751         var d = this.getPageData(),
24752             ap = d.activePage,
24753             ps = d.pages;
24754         
24755         
24756         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24757         this.field.dom.value = ap;
24758         this.first.setDisabled(ap == 1);
24759         this.prev.setDisabled(ap == 1);
24760         this.next.setDisabled(ap == ps);
24761         this.last.setDisabled(ap == ps);
24762         this.loading.enable();
24763         this.updateInfo();
24764     },
24765
24766     // private
24767     getPageData : function(){
24768         var total = this.ds.getTotalCount();
24769         return {
24770             total : total,
24771             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24772             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24773         };
24774     },
24775
24776     // private
24777     onLoadError : function(){
24778         this.loading.enable();
24779     },
24780
24781     // private
24782     onPagingKeydown : function(e){
24783         var k = e.getKey();
24784         var d = this.getPageData();
24785         if(k == e.RETURN){
24786             var v = this.field.dom.value, pageNum;
24787             if(!v || isNaN(pageNum = parseInt(v, 10))){
24788                 this.field.dom.value = d.activePage;
24789                 return;
24790             }
24791             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24792             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24793             e.stopEvent();
24794         }
24795         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))
24796         {
24797           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24798           this.field.dom.value = pageNum;
24799           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24800           e.stopEvent();
24801         }
24802         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24803         {
24804           var v = this.field.dom.value, pageNum; 
24805           var increment = (e.shiftKey) ? 10 : 1;
24806           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24807                 increment *= -1;
24808           }
24809           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24810             this.field.dom.value = d.activePage;
24811             return;
24812           }
24813           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24814           {
24815             this.field.dom.value = parseInt(v, 10) + increment;
24816             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24817             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24818           }
24819           e.stopEvent();
24820         }
24821     },
24822
24823     // private
24824     beforeLoad : function(){
24825         if(this.loading){
24826             this.loading.disable();
24827         }
24828     },
24829
24830     // private
24831     onClick : function(which){
24832         
24833         var ds = this.ds;
24834         if (!ds) {
24835             return;
24836         }
24837         
24838         switch(which){
24839             case "first":
24840                 ds.load({params:{start: 0, limit: this.pageSize}});
24841             break;
24842             case "prev":
24843                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24844             break;
24845             case "next":
24846                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24847             break;
24848             case "last":
24849                 var total = ds.getTotalCount();
24850                 var extra = total % this.pageSize;
24851                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24852                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24853             break;
24854             case "refresh":
24855                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24856             break;
24857         }
24858     },
24859
24860     /**
24861      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24862      * @param {Roo.data.Store} store The data store to unbind
24863      */
24864     unbind : function(ds){
24865         ds.un("beforeload", this.beforeLoad, this);
24866         ds.un("load", this.onLoad, this);
24867         ds.un("loadexception", this.onLoadError, this);
24868         ds.un("remove", this.updateInfo, this);
24869         ds.un("add", this.updateInfo, this);
24870         this.ds = undefined;
24871     },
24872
24873     /**
24874      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24875      * @param {Roo.data.Store} store The data store to bind
24876      */
24877     bind : function(ds){
24878         ds.on("beforeload", this.beforeLoad, this);
24879         ds.on("load", this.onLoad, this);
24880         ds.on("loadexception", this.onLoadError, this);
24881         ds.on("remove", this.updateInfo, this);
24882         ds.on("add", this.updateInfo, this);
24883         this.ds = ds;
24884     }
24885 });/*
24886  * - LGPL
24887  *
24888  * element
24889  * 
24890  */
24891
24892 /**
24893  * @class Roo.bootstrap.MessageBar
24894  * @extends Roo.bootstrap.Component
24895  * Bootstrap MessageBar class
24896  * @cfg {String} html contents of the MessageBar
24897  * @cfg {String} weight (info | success | warning | danger) default info
24898  * @cfg {String} beforeClass insert the bar before the given class
24899  * @cfg {Boolean} closable (true | false) default false
24900  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24901  * 
24902  * @constructor
24903  * Create a new Element
24904  * @param {Object} config The config object
24905  */
24906
24907 Roo.bootstrap.MessageBar = function(config){
24908     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24909 };
24910
24911 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24912     
24913     html: '',
24914     weight: 'info',
24915     closable: false,
24916     fixed: false,
24917     beforeClass: 'bootstrap-sticky-wrap',
24918     
24919     getAutoCreate : function(){
24920         
24921         var cfg = {
24922             tag: 'div',
24923             cls: 'alert alert-dismissable alert-' + this.weight,
24924             cn: [
24925                 {
24926                     tag: 'span',
24927                     cls: 'message',
24928                     html: this.html || ''
24929                 }
24930             ]
24931         };
24932         
24933         if(this.fixed){
24934             cfg.cls += ' alert-messages-fixed';
24935         }
24936         
24937         if(this.closable){
24938             cfg.cn.push({
24939                 tag: 'button',
24940                 cls: 'close',
24941                 html: 'x'
24942             });
24943         }
24944         
24945         return cfg;
24946     },
24947     
24948     onRender : function(ct, position)
24949     {
24950         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24951         
24952         if(!this.el){
24953             var cfg = Roo.apply({},  this.getAutoCreate());
24954             cfg.id = Roo.id();
24955             
24956             if (this.cls) {
24957                 cfg.cls += ' ' + this.cls;
24958             }
24959             if (this.style) {
24960                 cfg.style = this.style;
24961             }
24962             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24963             
24964             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24965         }
24966         
24967         this.el.select('>button.close').on('click', this.hide, this);
24968         
24969     },
24970     
24971     show : function()
24972     {
24973         if (!this.rendered) {
24974             this.render();
24975         }
24976         
24977         this.el.show();
24978         
24979         this.fireEvent('show', this);
24980         
24981     },
24982     
24983     hide : function()
24984     {
24985         if (!this.rendered) {
24986             this.render();
24987         }
24988         
24989         this.el.hide();
24990         
24991         this.fireEvent('hide', this);
24992     },
24993     
24994     update : function()
24995     {
24996 //        var e = this.el.dom.firstChild;
24997 //        
24998 //        if(this.closable){
24999 //            e = e.nextSibling;
25000 //        }
25001 //        
25002 //        e.data = this.html || '';
25003
25004         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25005     }
25006    
25007 });
25008
25009  
25010
25011      /*
25012  * - LGPL
25013  *
25014  * Graph
25015  * 
25016  */
25017
25018
25019 /**
25020  * @class Roo.bootstrap.Graph
25021  * @extends Roo.bootstrap.Component
25022  * Bootstrap Graph class
25023 > Prameters
25024  -sm {number} sm 4
25025  -md {number} md 5
25026  @cfg {String} graphtype  bar | vbar | pie
25027  @cfg {number} g_x coodinator | centre x (pie)
25028  @cfg {number} g_y coodinator | centre y (pie)
25029  @cfg {number} g_r radius (pie)
25030  @cfg {number} g_height height of the chart (respected by all elements in the set)
25031  @cfg {number} g_width width of the chart (respected by all elements in the set)
25032  @cfg {Object} title The title of the chart
25033     
25034  -{Array}  values
25035  -opts (object) options for the chart 
25036      o {
25037      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25038      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25039      o vgutter (number)
25040      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.
25041      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25042      o to
25043      o stretch (boolean)
25044      o }
25045  -opts (object) options for the pie
25046      o{
25047      o cut
25048      o startAngle (number)
25049      o endAngle (number)
25050      } 
25051  *
25052  * @constructor
25053  * Create a new Input
25054  * @param {Object} config The config object
25055  */
25056
25057 Roo.bootstrap.Graph = function(config){
25058     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25059     
25060     this.addEvents({
25061         // img events
25062         /**
25063          * @event click
25064          * The img click event for the img.
25065          * @param {Roo.EventObject} e
25066          */
25067         "click" : true
25068     });
25069 };
25070
25071 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25072     
25073     sm: 4,
25074     md: 5,
25075     graphtype: 'bar',
25076     g_height: 250,
25077     g_width: 400,
25078     g_x: 50,
25079     g_y: 50,
25080     g_r: 30,
25081     opts:{
25082         //g_colors: this.colors,
25083         g_type: 'soft',
25084         g_gutter: '20%'
25085
25086     },
25087     title : false,
25088
25089     getAutoCreate : function(){
25090         
25091         var cfg = {
25092             tag: 'div',
25093             html : null
25094         };
25095         
25096         
25097         return  cfg;
25098     },
25099
25100     onRender : function(ct,position){
25101         
25102         
25103         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25104         
25105         if (typeof(Raphael) == 'undefined') {
25106             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25107             return;
25108         }
25109         
25110         this.raphael = Raphael(this.el.dom);
25111         
25112                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25113                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25114                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25115                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25116                 /*
25117                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25118                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25119                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25120                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25121                 
25122                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25123                 r.barchart(330, 10, 300, 220, data1);
25124                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25125                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25126                 */
25127                 
25128                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25129                 // r.barchart(30, 30, 560, 250,  xdata, {
25130                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25131                 //     axis : "0 0 1 1",
25132                 //     axisxlabels :  xdata
25133                 //     //yvalues : cols,
25134                    
25135                 // });
25136 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25137 //        
25138 //        this.load(null,xdata,{
25139 //                axis : "0 0 1 1",
25140 //                axisxlabels :  xdata
25141 //                });
25142
25143     },
25144
25145     load : function(graphtype,xdata,opts)
25146     {
25147         this.raphael.clear();
25148         if(!graphtype) {
25149             graphtype = this.graphtype;
25150         }
25151         if(!opts){
25152             opts = this.opts;
25153         }
25154         var r = this.raphael,
25155             fin = function () {
25156                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25157             },
25158             fout = function () {
25159                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25160             },
25161             pfin = function() {
25162                 this.sector.stop();
25163                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25164
25165                 if (this.label) {
25166                     this.label[0].stop();
25167                     this.label[0].attr({ r: 7.5 });
25168                     this.label[1].attr({ "font-weight": 800 });
25169                 }
25170             },
25171             pfout = function() {
25172                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25173
25174                 if (this.label) {
25175                     this.label[0].animate({ r: 5 }, 500, "bounce");
25176                     this.label[1].attr({ "font-weight": 400 });
25177                 }
25178             };
25179
25180         switch(graphtype){
25181             case 'bar':
25182                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25183                 break;
25184             case 'hbar':
25185                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25186                 break;
25187             case 'pie':
25188 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25189 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25190 //            
25191                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25192                 
25193                 break;
25194
25195         }
25196         
25197         if(this.title){
25198             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25199         }
25200         
25201     },
25202     
25203     setTitle: function(o)
25204     {
25205         this.title = o;
25206     },
25207     
25208     initEvents: function() {
25209         
25210         if(!this.href){
25211             this.el.on('click', this.onClick, this);
25212         }
25213     },
25214     
25215     onClick : function(e)
25216     {
25217         Roo.log('img onclick');
25218         this.fireEvent('click', this, e);
25219     }
25220    
25221 });
25222
25223  
25224 /*
25225  * - LGPL
25226  *
25227  * numberBox
25228  * 
25229  */
25230 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25231
25232 /**
25233  * @class Roo.bootstrap.dash.NumberBox
25234  * @extends Roo.bootstrap.Component
25235  * Bootstrap NumberBox class
25236  * @cfg {String} headline Box headline
25237  * @cfg {String} content Box content
25238  * @cfg {String} icon Box icon
25239  * @cfg {String} footer Footer text
25240  * @cfg {String} fhref Footer href
25241  * 
25242  * @constructor
25243  * Create a new NumberBox
25244  * @param {Object} config The config object
25245  */
25246
25247
25248 Roo.bootstrap.dash.NumberBox = function(config){
25249     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25250     
25251 };
25252
25253 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25254     
25255     headline : '',
25256     content : '',
25257     icon : '',
25258     footer : '',
25259     fhref : '',
25260     ficon : '',
25261     
25262     getAutoCreate : function(){
25263         
25264         var cfg = {
25265             tag : 'div',
25266             cls : 'small-box ',
25267             cn : [
25268                 {
25269                     tag : 'div',
25270                     cls : 'inner',
25271                     cn :[
25272                         {
25273                             tag : 'h3',
25274                             cls : 'roo-headline',
25275                             html : this.headline
25276                         },
25277                         {
25278                             tag : 'p',
25279                             cls : 'roo-content',
25280                             html : this.content
25281                         }
25282                     ]
25283                 }
25284             ]
25285         };
25286         
25287         if(this.icon){
25288             cfg.cn.push({
25289                 tag : 'div',
25290                 cls : 'icon',
25291                 cn :[
25292                     {
25293                         tag : 'i',
25294                         cls : 'ion ' + this.icon
25295                     }
25296                 ]
25297             });
25298         }
25299         
25300         if(this.footer){
25301             var footer = {
25302                 tag : 'a',
25303                 cls : 'small-box-footer',
25304                 href : this.fhref || '#',
25305                 html : this.footer
25306             };
25307             
25308             cfg.cn.push(footer);
25309             
25310         }
25311         
25312         return  cfg;
25313     },
25314
25315     onRender : function(ct,position){
25316         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25317
25318
25319        
25320                 
25321     },
25322
25323     setHeadline: function (value)
25324     {
25325         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25326     },
25327     
25328     setFooter: function (value, href)
25329     {
25330         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25331         
25332         if(href){
25333             this.el.select('a.small-box-footer',true).first().attr('href', href);
25334         }
25335         
25336     },
25337
25338     setContent: function (value)
25339     {
25340         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25341     },
25342
25343     initEvents: function() 
25344     {   
25345         
25346     }
25347     
25348 });
25349
25350  
25351 /*
25352  * - LGPL
25353  *
25354  * TabBox
25355  * 
25356  */
25357 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25358
25359 /**
25360  * @class Roo.bootstrap.dash.TabBox
25361  * @extends Roo.bootstrap.Component
25362  * Bootstrap TabBox class
25363  * @cfg {String} title Title of the TabBox
25364  * @cfg {String} icon Icon of the TabBox
25365  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25366  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25367  * 
25368  * @constructor
25369  * Create a new TabBox
25370  * @param {Object} config The config object
25371  */
25372
25373
25374 Roo.bootstrap.dash.TabBox = function(config){
25375     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25376     this.addEvents({
25377         // raw events
25378         /**
25379          * @event addpane
25380          * When a pane is added
25381          * @param {Roo.bootstrap.dash.TabPane} pane
25382          */
25383         "addpane" : true,
25384         /**
25385          * @event activatepane
25386          * When a pane is activated
25387          * @param {Roo.bootstrap.dash.TabPane} pane
25388          */
25389         "activatepane" : true
25390         
25391          
25392     });
25393     
25394     this.panes = [];
25395 };
25396
25397 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25398
25399     title : '',
25400     icon : false,
25401     showtabs : true,
25402     tabScrollable : false,
25403     
25404     getChildContainer : function()
25405     {
25406         return this.el.select('.tab-content', true).first();
25407     },
25408     
25409     getAutoCreate : function(){
25410         
25411         var header = {
25412             tag: 'li',
25413             cls: 'pull-left header',
25414             html: this.title,
25415             cn : []
25416         };
25417         
25418         if(this.icon){
25419             header.cn.push({
25420                 tag: 'i',
25421                 cls: 'fa ' + this.icon
25422             });
25423         }
25424         
25425         var h = {
25426             tag: 'ul',
25427             cls: 'nav nav-tabs pull-right',
25428             cn: [
25429                 header
25430             ]
25431         };
25432         
25433         if(this.tabScrollable){
25434             h = {
25435                 tag: 'div',
25436                 cls: 'tab-header',
25437                 cn: [
25438                     {
25439                         tag: 'ul',
25440                         cls: 'nav nav-tabs pull-right',
25441                         cn: [
25442                             header
25443                         ]
25444                     }
25445                 ]
25446             };
25447         }
25448         
25449         var cfg = {
25450             tag: 'div',
25451             cls: 'nav-tabs-custom',
25452             cn: [
25453                 h,
25454                 {
25455                     tag: 'div',
25456                     cls: 'tab-content no-padding',
25457                     cn: []
25458                 }
25459             ]
25460         };
25461
25462         return  cfg;
25463     },
25464     initEvents : function()
25465     {
25466         //Roo.log('add add pane handler');
25467         this.on('addpane', this.onAddPane, this);
25468     },
25469      /**
25470      * Updates the box title
25471      * @param {String} html to set the title to.
25472      */
25473     setTitle : function(value)
25474     {
25475         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25476     },
25477     onAddPane : function(pane)
25478     {
25479         this.panes.push(pane);
25480         //Roo.log('addpane');
25481         //Roo.log(pane);
25482         // tabs are rendere left to right..
25483         if(!this.showtabs){
25484             return;
25485         }
25486         
25487         var ctr = this.el.select('.nav-tabs', true).first();
25488          
25489          
25490         var existing = ctr.select('.nav-tab',true);
25491         var qty = existing.getCount();;
25492         
25493         
25494         var tab = ctr.createChild({
25495             tag : 'li',
25496             cls : 'nav-tab' + (qty ? '' : ' active'),
25497             cn : [
25498                 {
25499                     tag : 'a',
25500                     href:'#',
25501                     html : pane.title
25502                 }
25503             ]
25504         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25505         pane.tab = tab;
25506         
25507         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25508         if (!qty) {
25509             pane.el.addClass('active');
25510         }
25511         
25512                 
25513     },
25514     onTabClick : function(ev,un,ob,pane)
25515     {
25516         //Roo.log('tab - prev default');
25517         ev.preventDefault();
25518         
25519         
25520         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25521         pane.tab.addClass('active');
25522         //Roo.log(pane.title);
25523         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25524         // technically we should have a deactivate event.. but maybe add later.
25525         // and it should not de-activate the selected tab...
25526         this.fireEvent('activatepane', pane);
25527         pane.el.addClass('active');
25528         pane.fireEvent('activate');
25529         
25530         
25531     },
25532     
25533     getActivePane : function()
25534     {
25535         var r = false;
25536         Roo.each(this.panes, function(p) {
25537             if(p.el.hasClass('active')){
25538                 r = p;
25539                 return false;
25540             }
25541             
25542             return;
25543         });
25544         
25545         return r;
25546     }
25547     
25548     
25549 });
25550
25551  
25552 /*
25553  * - LGPL
25554  *
25555  * Tab pane
25556  * 
25557  */
25558 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25559 /**
25560  * @class Roo.bootstrap.TabPane
25561  * @extends Roo.bootstrap.Component
25562  * Bootstrap TabPane class
25563  * @cfg {Boolean} active (false | true) Default false
25564  * @cfg {String} title title of panel
25565
25566  * 
25567  * @constructor
25568  * Create a new TabPane
25569  * @param {Object} config The config object
25570  */
25571
25572 Roo.bootstrap.dash.TabPane = function(config){
25573     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25574     
25575     this.addEvents({
25576         // raw events
25577         /**
25578          * @event activate
25579          * When a pane is activated
25580          * @param {Roo.bootstrap.dash.TabPane} pane
25581          */
25582         "activate" : true
25583          
25584     });
25585 };
25586
25587 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25588     
25589     active : false,
25590     title : '',
25591     
25592     // the tabBox that this is attached to.
25593     tab : false,
25594      
25595     getAutoCreate : function() 
25596     {
25597         var cfg = {
25598             tag: 'div',
25599             cls: 'tab-pane'
25600         };
25601         
25602         if(this.active){
25603             cfg.cls += ' active';
25604         }
25605         
25606         return cfg;
25607     },
25608     initEvents  : function()
25609     {
25610         //Roo.log('trigger add pane handler');
25611         this.parent().fireEvent('addpane', this)
25612     },
25613     
25614      /**
25615      * Updates the tab title 
25616      * @param {String} html to set the title to.
25617      */
25618     setTitle: function(str)
25619     {
25620         if (!this.tab) {
25621             return;
25622         }
25623         this.title = str;
25624         this.tab.select('a', true).first().dom.innerHTML = str;
25625         
25626     }
25627     
25628     
25629     
25630 });
25631
25632  
25633
25634
25635  /*
25636  * - LGPL
25637  *
25638  * menu
25639  * 
25640  */
25641 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25642
25643 /**
25644  * @class Roo.bootstrap.menu.Menu
25645  * @extends Roo.bootstrap.Component
25646  * Bootstrap Menu class - container for Menu
25647  * @cfg {String} html Text of the menu
25648  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25649  * @cfg {String} icon Font awesome icon
25650  * @cfg {String} pos Menu align to (top | bottom) default bottom
25651  * 
25652  * 
25653  * @constructor
25654  * Create a new Menu
25655  * @param {Object} config The config object
25656  */
25657
25658
25659 Roo.bootstrap.menu.Menu = function(config){
25660     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25661     
25662     this.addEvents({
25663         /**
25664          * @event beforeshow
25665          * Fires before this menu is displayed
25666          * @param {Roo.bootstrap.menu.Menu} this
25667          */
25668         beforeshow : true,
25669         /**
25670          * @event beforehide
25671          * Fires before this menu is hidden
25672          * @param {Roo.bootstrap.menu.Menu} this
25673          */
25674         beforehide : true,
25675         /**
25676          * @event show
25677          * Fires after this menu is displayed
25678          * @param {Roo.bootstrap.menu.Menu} this
25679          */
25680         show : true,
25681         /**
25682          * @event hide
25683          * Fires after this menu is hidden
25684          * @param {Roo.bootstrap.menu.Menu} this
25685          */
25686         hide : true,
25687         /**
25688          * @event click
25689          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25690          * @param {Roo.bootstrap.menu.Menu} this
25691          * @param {Roo.EventObject} e
25692          */
25693         click : true
25694     });
25695     
25696 };
25697
25698 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25699     
25700     submenu : false,
25701     html : '',
25702     weight : 'default',
25703     icon : false,
25704     pos : 'bottom',
25705     
25706     
25707     getChildContainer : function() {
25708         if(this.isSubMenu){
25709             return this.el;
25710         }
25711         
25712         return this.el.select('ul.dropdown-menu', true).first();  
25713     },
25714     
25715     getAutoCreate : function()
25716     {
25717         var text = [
25718             {
25719                 tag : 'span',
25720                 cls : 'roo-menu-text',
25721                 html : this.html
25722             }
25723         ];
25724         
25725         if(this.icon){
25726             text.unshift({
25727                 tag : 'i',
25728                 cls : 'fa ' + this.icon
25729             })
25730         }
25731         
25732         
25733         var cfg = {
25734             tag : 'div',
25735             cls : 'btn-group',
25736             cn : [
25737                 {
25738                     tag : 'button',
25739                     cls : 'dropdown-button btn btn-' + this.weight,
25740                     cn : text
25741                 },
25742                 {
25743                     tag : 'button',
25744                     cls : 'dropdown-toggle btn btn-' + this.weight,
25745                     cn : [
25746                         {
25747                             tag : 'span',
25748                             cls : 'caret'
25749                         }
25750                     ]
25751                 },
25752                 {
25753                     tag : 'ul',
25754                     cls : 'dropdown-menu'
25755                 }
25756             ]
25757             
25758         };
25759         
25760         if(this.pos == 'top'){
25761             cfg.cls += ' dropup';
25762         }
25763         
25764         if(this.isSubMenu){
25765             cfg = {
25766                 tag : 'ul',
25767                 cls : 'dropdown-menu'
25768             }
25769         }
25770         
25771         return cfg;
25772     },
25773     
25774     onRender : function(ct, position)
25775     {
25776         this.isSubMenu = ct.hasClass('dropdown-submenu');
25777         
25778         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25779     },
25780     
25781     initEvents : function() 
25782     {
25783         if(this.isSubMenu){
25784             return;
25785         }
25786         
25787         this.hidden = true;
25788         
25789         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25790         this.triggerEl.on('click', this.onTriggerPress, this);
25791         
25792         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25793         this.buttonEl.on('click', this.onClick, this);
25794         
25795     },
25796     
25797     list : function()
25798     {
25799         if(this.isSubMenu){
25800             return this.el;
25801         }
25802         
25803         return this.el.select('ul.dropdown-menu', true).first();
25804     },
25805     
25806     onClick : function(e)
25807     {
25808         this.fireEvent("click", this, e);
25809     },
25810     
25811     onTriggerPress  : function(e)
25812     {   
25813         if (this.isVisible()) {
25814             this.hide();
25815         } else {
25816             this.show();
25817         }
25818     },
25819     
25820     isVisible : function(){
25821         return !this.hidden;
25822     },
25823     
25824     show : function()
25825     {
25826         this.fireEvent("beforeshow", this);
25827         
25828         this.hidden = false;
25829         this.el.addClass('open');
25830         
25831         Roo.get(document).on("mouseup", this.onMouseUp, this);
25832         
25833         this.fireEvent("show", this);
25834         
25835         
25836     },
25837     
25838     hide : function()
25839     {
25840         this.fireEvent("beforehide", this);
25841         
25842         this.hidden = true;
25843         this.el.removeClass('open');
25844         
25845         Roo.get(document).un("mouseup", this.onMouseUp);
25846         
25847         this.fireEvent("hide", this);
25848     },
25849     
25850     onMouseUp : function()
25851     {
25852         this.hide();
25853     }
25854     
25855 });
25856
25857  
25858  /*
25859  * - LGPL
25860  *
25861  * menu item
25862  * 
25863  */
25864 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25865
25866 /**
25867  * @class Roo.bootstrap.menu.Item
25868  * @extends Roo.bootstrap.Component
25869  * Bootstrap MenuItem class
25870  * @cfg {Boolean} submenu (true | false) default false
25871  * @cfg {String} html text of the item
25872  * @cfg {String} href the link
25873  * @cfg {Boolean} disable (true | false) default false
25874  * @cfg {Boolean} preventDefault (true | false) default true
25875  * @cfg {String} icon Font awesome icon
25876  * @cfg {String} pos Submenu align to (left | right) default right 
25877  * 
25878  * 
25879  * @constructor
25880  * Create a new Item
25881  * @param {Object} config The config object
25882  */
25883
25884
25885 Roo.bootstrap.menu.Item = function(config){
25886     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25887     this.addEvents({
25888         /**
25889          * @event mouseover
25890          * Fires when the mouse is hovering over this menu
25891          * @param {Roo.bootstrap.menu.Item} this
25892          * @param {Roo.EventObject} e
25893          */
25894         mouseover : true,
25895         /**
25896          * @event mouseout
25897          * Fires when the mouse exits this menu
25898          * @param {Roo.bootstrap.menu.Item} this
25899          * @param {Roo.EventObject} e
25900          */
25901         mouseout : true,
25902         // raw events
25903         /**
25904          * @event click
25905          * The raw click event for the entire grid.
25906          * @param {Roo.EventObject} e
25907          */
25908         click : true
25909     });
25910 };
25911
25912 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25913     
25914     submenu : false,
25915     href : '',
25916     html : '',
25917     preventDefault: true,
25918     disable : false,
25919     icon : false,
25920     pos : 'right',
25921     
25922     getAutoCreate : function()
25923     {
25924         var text = [
25925             {
25926                 tag : 'span',
25927                 cls : 'roo-menu-item-text',
25928                 html : this.html
25929             }
25930         ];
25931         
25932         if(this.icon){
25933             text.unshift({
25934                 tag : 'i',
25935                 cls : 'fa ' + this.icon
25936             })
25937         }
25938         
25939         var cfg = {
25940             tag : 'li',
25941             cn : [
25942                 {
25943                     tag : 'a',
25944                     href : this.href || '#',
25945                     cn : text
25946                 }
25947             ]
25948         };
25949         
25950         if(this.disable){
25951             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25952         }
25953         
25954         if(this.submenu){
25955             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25956             
25957             if(this.pos == 'left'){
25958                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25959             }
25960         }
25961         
25962         return cfg;
25963     },
25964     
25965     initEvents : function() 
25966     {
25967         this.el.on('mouseover', this.onMouseOver, this);
25968         this.el.on('mouseout', this.onMouseOut, this);
25969         
25970         this.el.select('a', true).first().on('click', this.onClick, this);
25971         
25972     },
25973     
25974     onClick : function(e)
25975     {
25976         if(this.preventDefault){
25977             e.preventDefault();
25978         }
25979         
25980         this.fireEvent("click", this, e);
25981     },
25982     
25983     onMouseOver : function(e)
25984     {
25985         if(this.submenu && this.pos == 'left'){
25986             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25987         }
25988         
25989         this.fireEvent("mouseover", this, e);
25990     },
25991     
25992     onMouseOut : function(e)
25993     {
25994         this.fireEvent("mouseout", this, e);
25995     }
25996 });
25997
25998  
25999
26000  /*
26001  * - LGPL
26002  *
26003  * menu separator
26004  * 
26005  */
26006 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26007
26008 /**
26009  * @class Roo.bootstrap.menu.Separator
26010  * @extends Roo.bootstrap.Component
26011  * Bootstrap Separator class
26012  * 
26013  * @constructor
26014  * Create a new Separator
26015  * @param {Object} config The config object
26016  */
26017
26018
26019 Roo.bootstrap.menu.Separator = function(config){
26020     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26021 };
26022
26023 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26024     
26025     getAutoCreate : function(){
26026         var cfg = {
26027             tag : 'li',
26028             cls: 'divider'
26029         };
26030         
26031         return cfg;
26032     }
26033    
26034 });
26035
26036  
26037
26038  /*
26039  * - LGPL
26040  *
26041  * Tooltip
26042  * 
26043  */
26044
26045 /**
26046  * @class Roo.bootstrap.Tooltip
26047  * Bootstrap Tooltip class
26048  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26049  * to determine which dom element triggers the tooltip.
26050  * 
26051  * It needs to add support for additional attributes like tooltip-position
26052  * 
26053  * @constructor
26054  * Create a new Toolti
26055  * @param {Object} config The config object
26056  */
26057
26058 Roo.bootstrap.Tooltip = function(config){
26059     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26060     
26061     this.alignment = Roo.bootstrap.Tooltip.alignment;
26062     
26063     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26064         this.alignment = config.alignment;
26065     }
26066     
26067 };
26068
26069 Roo.apply(Roo.bootstrap.Tooltip, {
26070     /**
26071      * @function init initialize tooltip monitoring.
26072      * @static
26073      */
26074     currentEl : false,
26075     currentTip : false,
26076     currentRegion : false,
26077     
26078     //  init : delay?
26079     
26080     init : function()
26081     {
26082         Roo.get(document).on('mouseover', this.enter ,this);
26083         Roo.get(document).on('mouseout', this.leave, this);
26084          
26085         
26086         this.currentTip = new Roo.bootstrap.Tooltip();
26087     },
26088     
26089     enter : function(ev)
26090     {
26091         var dom = ev.getTarget();
26092         
26093         //Roo.log(['enter',dom]);
26094         var el = Roo.fly(dom);
26095         if (this.currentEl) {
26096             //Roo.log(dom);
26097             //Roo.log(this.currentEl);
26098             //Roo.log(this.currentEl.contains(dom));
26099             if (this.currentEl == el) {
26100                 return;
26101             }
26102             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26103                 return;
26104             }
26105
26106         }
26107         
26108         if (this.currentTip.el) {
26109             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26110         }    
26111         //Roo.log(ev);
26112         
26113         if(!el || el.dom == document){
26114             return;
26115         }
26116         
26117         var bindEl = el;
26118         
26119         // you can not look for children, as if el is the body.. then everythign is the child..
26120         if (!el.attr('tooltip')) { //
26121             if (!el.select("[tooltip]").elements.length) {
26122                 return;
26123             }
26124             // is the mouse over this child...?
26125             bindEl = el.select("[tooltip]").first();
26126             var xy = ev.getXY();
26127             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26128                 //Roo.log("not in region.");
26129                 return;
26130             }
26131             //Roo.log("child element over..");
26132             
26133         }
26134         this.currentEl = bindEl;
26135         this.currentTip.bind(bindEl);
26136         this.currentRegion = Roo.lib.Region.getRegion(dom);
26137         this.currentTip.enter();
26138         
26139     },
26140     leave : function(ev)
26141     {
26142         var dom = ev.getTarget();
26143         //Roo.log(['leave',dom]);
26144         if (!this.currentEl) {
26145             return;
26146         }
26147         
26148         
26149         if (dom != this.currentEl.dom) {
26150             return;
26151         }
26152         var xy = ev.getXY();
26153         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26154             return;
26155         }
26156         // only activate leave if mouse cursor is outside... bounding box..
26157         
26158         
26159         
26160         
26161         if (this.currentTip) {
26162             this.currentTip.leave();
26163         }
26164         //Roo.log('clear currentEl');
26165         this.currentEl = false;
26166         
26167         
26168     },
26169     alignment : {
26170         'left' : ['r-l', [-2,0], 'right'],
26171         'right' : ['l-r', [2,0], 'left'],
26172         'bottom' : ['t-b', [0,2], 'top'],
26173         'top' : [ 'b-t', [0,-2], 'bottom']
26174     }
26175     
26176 });
26177
26178
26179 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26180     
26181     
26182     bindEl : false,
26183     
26184     delay : null, // can be { show : 300 , hide: 500}
26185     
26186     timeout : null,
26187     
26188     hoverState : null, //???
26189     
26190     placement : 'bottom', 
26191     
26192     alignment : false,
26193     
26194     getAutoCreate : function(){
26195     
26196         var cfg = {
26197            cls : 'tooltip',
26198            role : 'tooltip',
26199            cn : [
26200                 {
26201                     cls : 'tooltip-arrow'
26202                 },
26203                 {
26204                     cls : 'tooltip-inner'
26205                 }
26206            ]
26207         };
26208         
26209         return cfg;
26210     },
26211     bind : function(el)
26212     {
26213         this.bindEl = el;
26214     },
26215       
26216     
26217     enter : function () {
26218        
26219         if (this.timeout != null) {
26220             clearTimeout(this.timeout);
26221         }
26222         
26223         this.hoverState = 'in';
26224          //Roo.log("enter - show");
26225         if (!this.delay || !this.delay.show) {
26226             this.show();
26227             return;
26228         }
26229         var _t = this;
26230         this.timeout = setTimeout(function () {
26231             if (_t.hoverState == 'in') {
26232                 _t.show();
26233             }
26234         }, this.delay.show);
26235     },
26236     leave : function()
26237     {
26238         clearTimeout(this.timeout);
26239     
26240         this.hoverState = 'out';
26241          if (!this.delay || !this.delay.hide) {
26242             this.hide();
26243             return;
26244         }
26245        
26246         var _t = this;
26247         this.timeout = setTimeout(function () {
26248             //Roo.log("leave - timeout");
26249             
26250             if (_t.hoverState == 'out') {
26251                 _t.hide();
26252                 Roo.bootstrap.Tooltip.currentEl = false;
26253             }
26254         }, delay);
26255     },
26256     
26257     show : function (msg)
26258     {
26259         if (!this.el) {
26260             this.render(document.body);
26261         }
26262         // set content.
26263         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26264         
26265         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26266         
26267         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26268         
26269         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26270         
26271         var placement = typeof this.placement == 'function' ?
26272             this.placement.call(this, this.el, on_el) :
26273             this.placement;
26274             
26275         var autoToken = /\s?auto?\s?/i;
26276         var autoPlace = autoToken.test(placement);
26277         if (autoPlace) {
26278             placement = placement.replace(autoToken, '') || 'top';
26279         }
26280         
26281         //this.el.detach()
26282         //this.el.setXY([0,0]);
26283         this.el.show();
26284         //this.el.dom.style.display='block';
26285         
26286         //this.el.appendTo(on_el);
26287         
26288         var p = this.getPosition();
26289         var box = this.el.getBox();
26290         
26291         if (autoPlace) {
26292             // fixme..
26293         }
26294         
26295         var align = this.alignment[placement];
26296         
26297         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26298         
26299         if(placement == 'top' || placement == 'bottom'){
26300             if(xy[0] < 0){
26301                 placement = 'right';
26302             }
26303             
26304             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26305                 placement = 'left';
26306             }
26307             
26308             var scroll = Roo.select('body', true).first().getScroll();
26309             
26310             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26311                 placement = 'top';
26312             }
26313             
26314             align = this.alignment[placement];
26315         }
26316         
26317         this.el.alignTo(this.bindEl, align[0],align[1]);
26318         //var arrow = this.el.select('.arrow',true).first();
26319         //arrow.set(align[2], 
26320         
26321         this.el.addClass(placement);
26322         
26323         this.el.addClass('in fade');
26324         
26325         this.hoverState = null;
26326         
26327         if (this.el.hasClass('fade')) {
26328             // fade it?
26329         }
26330         
26331     },
26332     hide : function()
26333     {
26334          
26335         if (!this.el) {
26336             return;
26337         }
26338         //this.el.setXY([0,0]);
26339         this.el.removeClass('in');
26340         //this.el.hide();
26341         
26342     }
26343     
26344 });
26345  
26346
26347  /*
26348  * - LGPL
26349  *
26350  * Location Picker
26351  * 
26352  */
26353
26354 /**
26355  * @class Roo.bootstrap.LocationPicker
26356  * @extends Roo.bootstrap.Component
26357  * Bootstrap LocationPicker class
26358  * @cfg {Number} latitude Position when init default 0
26359  * @cfg {Number} longitude Position when init default 0
26360  * @cfg {Number} zoom default 15
26361  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26362  * @cfg {Boolean} mapTypeControl default false
26363  * @cfg {Boolean} disableDoubleClickZoom default false
26364  * @cfg {Boolean} scrollwheel default true
26365  * @cfg {Boolean} streetViewControl default false
26366  * @cfg {Number} radius default 0
26367  * @cfg {String} locationName
26368  * @cfg {Boolean} draggable default true
26369  * @cfg {Boolean} enableAutocomplete default false
26370  * @cfg {Boolean} enableReverseGeocode default true
26371  * @cfg {String} markerTitle
26372  * 
26373  * @constructor
26374  * Create a new LocationPicker
26375  * @param {Object} config The config object
26376  */
26377
26378
26379 Roo.bootstrap.LocationPicker = function(config){
26380     
26381     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26382     
26383     this.addEvents({
26384         /**
26385          * @event initial
26386          * Fires when the picker initialized.
26387          * @param {Roo.bootstrap.LocationPicker} this
26388          * @param {Google Location} location
26389          */
26390         initial : true,
26391         /**
26392          * @event positionchanged
26393          * Fires when the picker position changed.
26394          * @param {Roo.bootstrap.LocationPicker} this
26395          * @param {Google Location} location
26396          */
26397         positionchanged : true,
26398         /**
26399          * @event resize
26400          * Fires when the map resize.
26401          * @param {Roo.bootstrap.LocationPicker} this
26402          */
26403         resize : true,
26404         /**
26405          * @event show
26406          * Fires when the map show.
26407          * @param {Roo.bootstrap.LocationPicker} this
26408          */
26409         show : true,
26410         /**
26411          * @event hide
26412          * Fires when the map hide.
26413          * @param {Roo.bootstrap.LocationPicker} this
26414          */
26415         hide : true,
26416         /**
26417          * @event mapClick
26418          * Fires when click the map.
26419          * @param {Roo.bootstrap.LocationPicker} this
26420          * @param {Map event} e
26421          */
26422         mapClick : true,
26423         /**
26424          * @event mapRightClick
26425          * Fires when right click the map.
26426          * @param {Roo.bootstrap.LocationPicker} this
26427          * @param {Map event} e
26428          */
26429         mapRightClick : true,
26430         /**
26431          * @event markerClick
26432          * Fires when click the marker.
26433          * @param {Roo.bootstrap.LocationPicker} this
26434          * @param {Map event} e
26435          */
26436         markerClick : true,
26437         /**
26438          * @event markerRightClick
26439          * Fires when right click the marker.
26440          * @param {Roo.bootstrap.LocationPicker} this
26441          * @param {Map event} e
26442          */
26443         markerRightClick : true,
26444         /**
26445          * @event OverlayViewDraw
26446          * Fires when OverlayView Draw
26447          * @param {Roo.bootstrap.LocationPicker} this
26448          */
26449         OverlayViewDraw : true,
26450         /**
26451          * @event OverlayViewOnAdd
26452          * Fires when OverlayView Draw
26453          * @param {Roo.bootstrap.LocationPicker} this
26454          */
26455         OverlayViewOnAdd : true,
26456         /**
26457          * @event OverlayViewOnRemove
26458          * Fires when OverlayView Draw
26459          * @param {Roo.bootstrap.LocationPicker} this
26460          */
26461         OverlayViewOnRemove : true,
26462         /**
26463          * @event OverlayViewShow
26464          * Fires when OverlayView Draw
26465          * @param {Roo.bootstrap.LocationPicker} this
26466          * @param {Pixel} cpx
26467          */
26468         OverlayViewShow : true,
26469         /**
26470          * @event OverlayViewHide
26471          * Fires when OverlayView Draw
26472          * @param {Roo.bootstrap.LocationPicker} this
26473          */
26474         OverlayViewHide : true,
26475         /**
26476          * @event loadexception
26477          * Fires when load google lib failed.
26478          * @param {Roo.bootstrap.LocationPicker} this
26479          */
26480         loadexception : true
26481     });
26482         
26483 };
26484
26485 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26486     
26487     gMapContext: false,
26488     
26489     latitude: 0,
26490     longitude: 0,
26491     zoom: 15,
26492     mapTypeId: false,
26493     mapTypeControl: false,
26494     disableDoubleClickZoom: false,
26495     scrollwheel: true,
26496     streetViewControl: false,
26497     radius: 0,
26498     locationName: '',
26499     draggable: true,
26500     enableAutocomplete: false,
26501     enableReverseGeocode: true,
26502     markerTitle: '',
26503     
26504     getAutoCreate: function()
26505     {
26506
26507         var cfg = {
26508             tag: 'div',
26509             cls: 'roo-location-picker'
26510         };
26511         
26512         return cfg
26513     },
26514     
26515     initEvents: function(ct, position)
26516     {       
26517         if(!this.el.getWidth() || this.isApplied()){
26518             return;
26519         }
26520         
26521         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26522         
26523         this.initial();
26524     },
26525     
26526     initial: function()
26527     {
26528         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26529             this.fireEvent('loadexception', this);
26530             return;
26531         }
26532         
26533         if(!this.mapTypeId){
26534             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26535         }
26536         
26537         this.gMapContext = this.GMapContext();
26538         
26539         this.initOverlayView();
26540         
26541         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26542         
26543         var _this = this;
26544                 
26545         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26546             _this.setPosition(_this.gMapContext.marker.position);
26547         });
26548         
26549         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26550             _this.fireEvent('mapClick', this, event);
26551             
26552         });
26553
26554         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26555             _this.fireEvent('mapRightClick', this, event);
26556             
26557         });
26558         
26559         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26560             _this.fireEvent('markerClick', this, event);
26561             
26562         });
26563
26564         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26565             _this.fireEvent('markerRightClick', this, event);
26566             
26567         });
26568         
26569         this.setPosition(this.gMapContext.location);
26570         
26571         this.fireEvent('initial', this, this.gMapContext.location);
26572     },
26573     
26574     initOverlayView: function()
26575     {
26576         var _this = this;
26577         
26578         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26579             
26580             draw: function()
26581             {
26582                 _this.fireEvent('OverlayViewDraw', _this);
26583             },
26584             
26585             onAdd: function()
26586             {
26587                 _this.fireEvent('OverlayViewOnAdd', _this);
26588             },
26589             
26590             onRemove: function()
26591             {
26592                 _this.fireEvent('OverlayViewOnRemove', _this);
26593             },
26594             
26595             show: function(cpx)
26596             {
26597                 _this.fireEvent('OverlayViewShow', _this, cpx);
26598             },
26599             
26600             hide: function()
26601             {
26602                 _this.fireEvent('OverlayViewHide', _this);
26603             }
26604             
26605         });
26606     },
26607     
26608     fromLatLngToContainerPixel: function(event)
26609     {
26610         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26611     },
26612     
26613     isApplied: function() 
26614     {
26615         return this.getGmapContext() == false ? false : true;
26616     },
26617     
26618     getGmapContext: function() 
26619     {
26620         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26621     },
26622     
26623     GMapContext: function() 
26624     {
26625         var position = new google.maps.LatLng(this.latitude, this.longitude);
26626         
26627         var _map = new google.maps.Map(this.el.dom, {
26628             center: position,
26629             zoom: this.zoom,
26630             mapTypeId: this.mapTypeId,
26631             mapTypeControl: this.mapTypeControl,
26632             disableDoubleClickZoom: this.disableDoubleClickZoom,
26633             scrollwheel: this.scrollwheel,
26634             streetViewControl: this.streetViewControl,
26635             locationName: this.locationName,
26636             draggable: this.draggable,
26637             enableAutocomplete: this.enableAutocomplete,
26638             enableReverseGeocode: this.enableReverseGeocode
26639         });
26640         
26641         var _marker = new google.maps.Marker({
26642             position: position,
26643             map: _map,
26644             title: this.markerTitle,
26645             draggable: this.draggable
26646         });
26647         
26648         return {
26649             map: _map,
26650             marker: _marker,
26651             circle: null,
26652             location: position,
26653             radius: this.radius,
26654             locationName: this.locationName,
26655             addressComponents: {
26656                 formatted_address: null,
26657                 addressLine1: null,
26658                 addressLine2: null,
26659                 streetName: null,
26660                 streetNumber: null,
26661                 city: null,
26662                 district: null,
26663                 state: null,
26664                 stateOrProvince: null
26665             },
26666             settings: this,
26667             domContainer: this.el.dom,
26668             geodecoder: new google.maps.Geocoder()
26669         };
26670     },
26671     
26672     drawCircle: function(center, radius, options) 
26673     {
26674         if (this.gMapContext.circle != null) {
26675             this.gMapContext.circle.setMap(null);
26676         }
26677         if (radius > 0) {
26678             radius *= 1;
26679             options = Roo.apply({}, options, {
26680                 strokeColor: "#0000FF",
26681                 strokeOpacity: .35,
26682                 strokeWeight: 2,
26683                 fillColor: "#0000FF",
26684                 fillOpacity: .2
26685             });
26686             
26687             options.map = this.gMapContext.map;
26688             options.radius = radius;
26689             options.center = center;
26690             this.gMapContext.circle = new google.maps.Circle(options);
26691             return this.gMapContext.circle;
26692         }
26693         
26694         return null;
26695     },
26696     
26697     setPosition: function(location) 
26698     {
26699         this.gMapContext.location = location;
26700         this.gMapContext.marker.setPosition(location);
26701         this.gMapContext.map.panTo(location);
26702         this.drawCircle(location, this.gMapContext.radius, {});
26703         
26704         var _this = this;
26705         
26706         if (this.gMapContext.settings.enableReverseGeocode) {
26707             this.gMapContext.geodecoder.geocode({
26708                 latLng: this.gMapContext.location
26709             }, function(results, status) {
26710                 
26711                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26712                     _this.gMapContext.locationName = results[0].formatted_address;
26713                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26714                     
26715                     _this.fireEvent('positionchanged', this, location);
26716                 }
26717             });
26718             
26719             return;
26720         }
26721         
26722         this.fireEvent('positionchanged', this, location);
26723     },
26724     
26725     resize: function()
26726     {
26727         google.maps.event.trigger(this.gMapContext.map, "resize");
26728         
26729         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26730         
26731         this.fireEvent('resize', this);
26732     },
26733     
26734     setPositionByLatLng: function(latitude, longitude)
26735     {
26736         this.setPosition(new google.maps.LatLng(latitude, longitude));
26737     },
26738     
26739     getCurrentPosition: function() 
26740     {
26741         return {
26742             latitude: this.gMapContext.location.lat(),
26743             longitude: this.gMapContext.location.lng()
26744         };
26745     },
26746     
26747     getAddressName: function() 
26748     {
26749         return this.gMapContext.locationName;
26750     },
26751     
26752     getAddressComponents: function() 
26753     {
26754         return this.gMapContext.addressComponents;
26755     },
26756     
26757     address_component_from_google_geocode: function(address_components) 
26758     {
26759         var result = {};
26760         
26761         for (var i = 0; i < address_components.length; i++) {
26762             var component = address_components[i];
26763             if (component.types.indexOf("postal_code") >= 0) {
26764                 result.postalCode = component.short_name;
26765             } else if (component.types.indexOf("street_number") >= 0) {
26766                 result.streetNumber = component.short_name;
26767             } else if (component.types.indexOf("route") >= 0) {
26768                 result.streetName = component.short_name;
26769             } else if (component.types.indexOf("neighborhood") >= 0) {
26770                 result.city = component.short_name;
26771             } else if (component.types.indexOf("locality") >= 0) {
26772                 result.city = component.short_name;
26773             } else if (component.types.indexOf("sublocality") >= 0) {
26774                 result.district = component.short_name;
26775             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26776                 result.stateOrProvince = component.short_name;
26777             } else if (component.types.indexOf("country") >= 0) {
26778                 result.country = component.short_name;
26779             }
26780         }
26781         
26782         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26783         result.addressLine2 = "";
26784         return result;
26785     },
26786     
26787     setZoomLevel: function(zoom)
26788     {
26789         this.gMapContext.map.setZoom(zoom);
26790     },
26791     
26792     show: function()
26793     {
26794         if(!this.el){
26795             return;
26796         }
26797         
26798         this.el.show();
26799         
26800         this.resize();
26801         
26802         this.fireEvent('show', this);
26803     },
26804     
26805     hide: function()
26806     {
26807         if(!this.el){
26808             return;
26809         }
26810         
26811         this.el.hide();
26812         
26813         this.fireEvent('hide', this);
26814     }
26815     
26816 });
26817
26818 Roo.apply(Roo.bootstrap.LocationPicker, {
26819     
26820     OverlayView : function(map, options)
26821     {
26822         options = options || {};
26823         
26824         this.setMap(map);
26825     }
26826     
26827     
26828 });/*
26829  * - LGPL
26830  *
26831  * Alert
26832  * 
26833  */
26834
26835 /**
26836  * @class Roo.bootstrap.Alert
26837  * @extends Roo.bootstrap.Component
26838  * Bootstrap Alert class
26839  * @cfg {String} title The title of alert
26840  * @cfg {String} html The content of alert
26841  * @cfg {String} weight (  success | info | warning | danger )
26842  * @cfg {String} faicon font-awesomeicon
26843  * 
26844  * @constructor
26845  * Create a new alert
26846  * @param {Object} config The config object
26847  */
26848
26849
26850 Roo.bootstrap.Alert = function(config){
26851     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26852     
26853 };
26854
26855 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26856     
26857     title: '',
26858     html: '',
26859     weight: false,
26860     faicon: false,
26861     
26862     getAutoCreate : function()
26863     {
26864         
26865         var cfg = {
26866             tag : 'div',
26867             cls : 'alert',
26868             cn : [
26869                 {
26870                     tag : 'i',
26871                     cls : 'roo-alert-icon'
26872                     
26873                 },
26874                 {
26875                     tag : 'b',
26876                     cls : 'roo-alert-title',
26877                     html : this.title
26878                 },
26879                 {
26880                     tag : 'span',
26881                     cls : 'roo-alert-text',
26882                     html : this.html
26883                 }
26884             ]
26885         };
26886         
26887         if(this.faicon){
26888             cfg.cn[0].cls += ' fa ' + this.faicon;
26889         }
26890         
26891         if(this.weight){
26892             cfg.cls += ' alert-' + this.weight;
26893         }
26894         
26895         return cfg;
26896     },
26897     
26898     initEvents: function() 
26899     {
26900         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26901     },
26902     
26903     setTitle : function(str)
26904     {
26905         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26906     },
26907     
26908     setText : function(str)
26909     {
26910         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26911     },
26912     
26913     setWeight : function(weight)
26914     {
26915         if(this.weight){
26916             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26917         }
26918         
26919         this.weight = weight;
26920         
26921         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26922     },
26923     
26924     setIcon : function(icon)
26925     {
26926         if(this.faicon){
26927             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26928         }
26929         
26930         this.faicon = icon;
26931         
26932         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26933     },
26934     
26935     hide: function() 
26936     {
26937         this.el.hide();   
26938     },
26939     
26940     show: function() 
26941     {  
26942         this.el.show();   
26943     }
26944     
26945 });
26946
26947  
26948 /*
26949 * Licence: LGPL
26950 */
26951
26952 /**
26953  * @class Roo.bootstrap.UploadCropbox
26954  * @extends Roo.bootstrap.Component
26955  * Bootstrap UploadCropbox class
26956  * @cfg {String} emptyText show when image has been loaded
26957  * @cfg {String} rotateNotify show when image too small to rotate
26958  * @cfg {Number} errorTimeout default 3000
26959  * @cfg {Number} minWidth default 300
26960  * @cfg {Number} minHeight default 300
26961  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26962  * @cfg {Boolean} isDocument (true|false) default false
26963  * @cfg {String} url action url
26964  * @cfg {String} paramName default 'imageUpload'
26965  * @cfg {String} method default POST
26966  * @cfg {Boolean} loadMask (true|false) default true
26967  * @cfg {Boolean} loadingText default 'Loading...'
26968  * 
26969  * @constructor
26970  * Create a new UploadCropbox
26971  * @param {Object} config The config object
26972  */
26973
26974 Roo.bootstrap.UploadCropbox = function(config){
26975     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26976     
26977     this.addEvents({
26978         /**
26979          * @event beforeselectfile
26980          * Fire before select file
26981          * @param {Roo.bootstrap.UploadCropbox} this
26982          */
26983         "beforeselectfile" : true,
26984         /**
26985          * @event initial
26986          * Fire after initEvent
26987          * @param {Roo.bootstrap.UploadCropbox} this
26988          */
26989         "initial" : true,
26990         /**
26991          * @event crop
26992          * Fire after initEvent
26993          * @param {Roo.bootstrap.UploadCropbox} this
26994          * @param {String} data
26995          */
26996         "crop" : true,
26997         /**
26998          * @event prepare
26999          * Fire when preparing the file data
27000          * @param {Roo.bootstrap.UploadCropbox} this
27001          * @param {Object} file
27002          */
27003         "prepare" : true,
27004         /**
27005          * @event exception
27006          * Fire when get exception
27007          * @param {Roo.bootstrap.UploadCropbox} this
27008          * @param {XMLHttpRequest} xhr
27009          */
27010         "exception" : true,
27011         /**
27012          * @event beforeloadcanvas
27013          * Fire before load the canvas
27014          * @param {Roo.bootstrap.UploadCropbox} this
27015          * @param {String} src
27016          */
27017         "beforeloadcanvas" : true,
27018         /**
27019          * @event trash
27020          * Fire when trash image
27021          * @param {Roo.bootstrap.UploadCropbox} this
27022          */
27023         "trash" : true,
27024         /**
27025          * @event download
27026          * Fire when download the image
27027          * @param {Roo.bootstrap.UploadCropbox} this
27028          */
27029         "download" : true,
27030         /**
27031          * @event footerbuttonclick
27032          * Fire when footerbuttonclick
27033          * @param {Roo.bootstrap.UploadCropbox} this
27034          * @param {String} type
27035          */
27036         "footerbuttonclick" : true,
27037         /**
27038          * @event resize
27039          * Fire when resize
27040          * @param {Roo.bootstrap.UploadCropbox} this
27041          */
27042         "resize" : true,
27043         /**
27044          * @event rotate
27045          * Fire when rotate the image
27046          * @param {Roo.bootstrap.UploadCropbox} this
27047          * @param {String} pos
27048          */
27049         "rotate" : true,
27050         /**
27051          * @event inspect
27052          * Fire when inspect the file
27053          * @param {Roo.bootstrap.UploadCropbox} this
27054          * @param {Object} file
27055          */
27056         "inspect" : true,
27057         /**
27058          * @event upload
27059          * Fire when xhr upload the file
27060          * @param {Roo.bootstrap.UploadCropbox} this
27061          * @param {Object} data
27062          */
27063         "upload" : true,
27064         /**
27065          * @event arrange
27066          * Fire when arrange the file data
27067          * @param {Roo.bootstrap.UploadCropbox} this
27068          * @param {Object} formData
27069          */
27070         "arrange" : true
27071     });
27072     
27073     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27074 };
27075
27076 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27077     
27078     emptyText : 'Click to upload image',
27079     rotateNotify : 'Image is too small to rotate',
27080     errorTimeout : 3000,
27081     scale : 0,
27082     baseScale : 1,
27083     rotate : 0,
27084     dragable : false,
27085     pinching : false,
27086     mouseX : 0,
27087     mouseY : 0,
27088     cropData : false,
27089     minWidth : 300,
27090     minHeight : 300,
27091     file : false,
27092     exif : {},
27093     baseRotate : 1,
27094     cropType : 'image/jpeg',
27095     buttons : false,
27096     canvasLoaded : false,
27097     isDocument : false,
27098     method : 'POST',
27099     paramName : 'imageUpload',
27100     loadMask : true,
27101     loadingText : 'Loading...',
27102     maskEl : false,
27103     
27104     getAutoCreate : function()
27105     {
27106         var cfg = {
27107             tag : 'div',
27108             cls : 'roo-upload-cropbox',
27109             cn : [
27110                 {
27111                     tag : 'input',
27112                     cls : 'roo-upload-cropbox-selector',
27113                     type : 'file'
27114                 },
27115                 {
27116                     tag : 'div',
27117                     cls : 'roo-upload-cropbox-body',
27118                     style : 'cursor:pointer',
27119                     cn : [
27120                         {
27121                             tag : 'div',
27122                             cls : 'roo-upload-cropbox-preview'
27123                         },
27124                         {
27125                             tag : 'div',
27126                             cls : 'roo-upload-cropbox-thumb'
27127                         },
27128                         {
27129                             tag : 'div',
27130                             cls : 'roo-upload-cropbox-empty-notify',
27131                             html : this.emptyText
27132                         },
27133                         {
27134                             tag : 'div',
27135                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27136                             html : this.rotateNotify
27137                         }
27138                     ]
27139                 },
27140                 {
27141                     tag : 'div',
27142                     cls : 'roo-upload-cropbox-footer',
27143                     cn : {
27144                         tag : 'div',
27145                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27146                         cn : []
27147                     }
27148                 }
27149             ]
27150         };
27151         
27152         return cfg;
27153     },
27154     
27155     onRender : function(ct, position)
27156     {
27157         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27158         
27159         if (this.buttons.length) {
27160             
27161             Roo.each(this.buttons, function(bb) {
27162                 
27163                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27164                 
27165                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27166                 
27167             }, this);
27168         }
27169         
27170         if(this.loadMask){
27171             this.maskEl = this.el;
27172         }
27173     },
27174     
27175     initEvents : function()
27176     {
27177         this.urlAPI = (window.createObjectURL && window) || 
27178                                 (window.URL && URL.revokeObjectURL && URL) || 
27179                                 (window.webkitURL && webkitURL);
27180                         
27181         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27182         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27183         
27184         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27185         this.selectorEl.hide();
27186         
27187         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27188         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27189         
27190         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27191         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27192         this.thumbEl.hide();
27193         
27194         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27195         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27196         
27197         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27198         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27199         this.errorEl.hide();
27200         
27201         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27202         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27203         this.footerEl.hide();
27204         
27205         this.setThumbBoxSize();
27206         
27207         this.bind();
27208         
27209         this.resize();
27210         
27211         this.fireEvent('initial', this);
27212     },
27213
27214     bind : function()
27215     {
27216         var _this = this;
27217         
27218         window.addEventListener("resize", function() { _this.resize(); } );
27219         
27220         this.bodyEl.on('click', this.beforeSelectFile, this);
27221         
27222         if(Roo.isTouch){
27223             this.bodyEl.on('touchstart', this.onTouchStart, this);
27224             this.bodyEl.on('touchmove', this.onTouchMove, this);
27225             this.bodyEl.on('touchend', this.onTouchEnd, this);
27226         }
27227         
27228         if(!Roo.isTouch){
27229             this.bodyEl.on('mousedown', this.onMouseDown, this);
27230             this.bodyEl.on('mousemove', this.onMouseMove, this);
27231             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27232             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27233             Roo.get(document).on('mouseup', this.onMouseUp, this);
27234         }
27235         
27236         this.selectorEl.on('change', this.onFileSelected, this);
27237     },
27238     
27239     reset : function()
27240     {    
27241         this.scale = 0;
27242         this.baseScale = 1;
27243         this.rotate = 0;
27244         this.baseRotate = 1;
27245         this.dragable = false;
27246         this.pinching = false;
27247         this.mouseX = 0;
27248         this.mouseY = 0;
27249         this.cropData = false;
27250         this.notifyEl.dom.innerHTML = this.emptyText;
27251         
27252         this.selectorEl.dom.value = '';
27253         
27254     },
27255     
27256     resize : function()
27257     {
27258         if(this.fireEvent('resize', this) != false){
27259             this.setThumbBoxPosition();
27260             this.setCanvasPosition();
27261         }
27262     },
27263     
27264     onFooterButtonClick : function(e, el, o, type)
27265     {
27266         switch (type) {
27267             case 'rotate-left' :
27268                 this.onRotateLeft(e);
27269                 break;
27270             case 'rotate-right' :
27271                 this.onRotateRight(e);
27272                 break;
27273             case 'picture' :
27274                 this.beforeSelectFile(e);
27275                 break;
27276             case 'trash' :
27277                 this.trash(e);
27278                 break;
27279             case 'crop' :
27280                 this.crop(e);
27281                 break;
27282             case 'download' :
27283                 this.download(e);
27284                 break;
27285             default :
27286                 break;
27287         }
27288         
27289         this.fireEvent('footerbuttonclick', this, type);
27290     },
27291     
27292     beforeSelectFile : function(e)
27293     {
27294         e.preventDefault();
27295         
27296         if(this.fireEvent('beforeselectfile', this) != false){
27297             this.selectorEl.dom.click();
27298         }
27299     },
27300     
27301     onFileSelected : function(e)
27302     {
27303         e.preventDefault();
27304         
27305         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27306             return;
27307         }
27308         
27309         var file = this.selectorEl.dom.files[0];
27310         
27311         if(this.fireEvent('inspect', this, file) != false){
27312             this.prepare(file);
27313         }
27314         
27315     },
27316     
27317     trash : function(e)
27318     {
27319         this.fireEvent('trash', this);
27320     },
27321     
27322     download : function(e)
27323     {
27324         this.fireEvent('download', this);
27325     },
27326     
27327     loadCanvas : function(src)
27328     {   
27329         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27330             
27331             this.reset();
27332             
27333             this.imageEl = document.createElement('img');
27334             
27335             var _this = this;
27336             
27337             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27338             
27339             this.imageEl.src = src;
27340         }
27341     },
27342     
27343     onLoadCanvas : function()
27344     {   
27345         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27346         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27347         
27348         this.bodyEl.un('click', this.beforeSelectFile, this);
27349         
27350         this.notifyEl.hide();
27351         this.thumbEl.show();
27352         this.footerEl.show();
27353         
27354         this.baseRotateLevel();
27355         
27356         if(this.isDocument){
27357             this.setThumbBoxSize();
27358         }
27359         
27360         this.setThumbBoxPosition();
27361         
27362         this.baseScaleLevel();
27363         
27364         this.draw();
27365         
27366         this.resize();
27367         
27368         this.canvasLoaded = true;
27369         
27370         if(this.loadMask){
27371             this.maskEl.unmask();
27372         }
27373         
27374     },
27375     
27376     setCanvasPosition : function()
27377     {   
27378         if(!this.canvasEl){
27379             return;
27380         }
27381         
27382         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27383         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27384         
27385         this.previewEl.setLeft(pw);
27386         this.previewEl.setTop(ph);
27387         
27388     },
27389     
27390     onMouseDown : function(e)
27391     {   
27392         e.stopEvent();
27393         
27394         this.dragable = true;
27395         this.pinching = false;
27396         
27397         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27398             this.dragable = false;
27399             return;
27400         }
27401         
27402         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27403         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27404         
27405     },
27406     
27407     onMouseMove : function(e)
27408     {   
27409         e.stopEvent();
27410         
27411         if(!this.canvasLoaded){
27412             return;
27413         }
27414         
27415         if (!this.dragable){
27416             return;
27417         }
27418         
27419         var minX = Math.ceil(this.thumbEl.getLeft(true));
27420         var minY = Math.ceil(this.thumbEl.getTop(true));
27421         
27422         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27423         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27424         
27425         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27426         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27427         
27428         x = x - this.mouseX;
27429         y = y - this.mouseY;
27430         
27431         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27432         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27433         
27434         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27435         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27436         
27437         this.previewEl.setLeft(bgX);
27438         this.previewEl.setTop(bgY);
27439         
27440         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27441         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27442     },
27443     
27444     onMouseUp : function(e)
27445     {   
27446         e.stopEvent();
27447         
27448         this.dragable = false;
27449     },
27450     
27451     onMouseWheel : function(e)
27452     {   
27453         e.stopEvent();
27454         
27455         this.startScale = this.scale;
27456         
27457         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27458         
27459         if(!this.zoomable()){
27460             this.scale = this.startScale;
27461             return;
27462         }
27463         
27464         this.draw();
27465         
27466         return;
27467     },
27468     
27469     zoomable : function()
27470     {
27471         var minScale = this.thumbEl.getWidth() / this.minWidth;
27472         
27473         if(this.minWidth < this.minHeight){
27474             minScale = this.thumbEl.getHeight() / this.minHeight;
27475         }
27476         
27477         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27478         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27479         
27480         if(
27481                 this.isDocument &&
27482                 (this.rotate == 0 || this.rotate == 180) && 
27483                 (
27484                     width > this.imageEl.OriginWidth || 
27485                     height > this.imageEl.OriginHeight ||
27486                     (width < this.minWidth && height < this.minHeight)
27487                 )
27488         ){
27489             return false;
27490         }
27491         
27492         if(
27493                 this.isDocument &&
27494                 (this.rotate == 90 || this.rotate == 270) && 
27495                 (
27496                     width > this.imageEl.OriginWidth || 
27497                     height > this.imageEl.OriginHeight ||
27498                     (width < this.minHeight && height < this.minWidth)
27499                 )
27500         ){
27501             return false;
27502         }
27503         
27504         if(
27505                 !this.isDocument &&
27506                 (this.rotate == 0 || this.rotate == 180) && 
27507                 (
27508                     width < this.minWidth || 
27509                     width > this.imageEl.OriginWidth || 
27510                     height < this.minHeight || 
27511                     height > this.imageEl.OriginHeight
27512                 )
27513         ){
27514             return false;
27515         }
27516         
27517         if(
27518                 !this.isDocument &&
27519                 (this.rotate == 90 || this.rotate == 270) && 
27520                 (
27521                     width < this.minHeight || 
27522                     width > this.imageEl.OriginWidth || 
27523                     height < this.minWidth || 
27524                     height > this.imageEl.OriginHeight
27525                 )
27526         ){
27527             return false;
27528         }
27529         
27530         return true;
27531         
27532     },
27533     
27534     onRotateLeft : function(e)
27535     {   
27536         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27537             
27538             var minScale = this.thumbEl.getWidth() / this.minWidth;
27539             
27540             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27541             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27542             
27543             this.startScale = this.scale;
27544             
27545             while (this.getScaleLevel() < minScale){
27546             
27547                 this.scale = this.scale + 1;
27548                 
27549                 if(!this.zoomable()){
27550                     break;
27551                 }
27552                 
27553                 if(
27554                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27555                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27556                 ){
27557                     continue;
27558                 }
27559                 
27560                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27561
27562                 this.draw();
27563                 
27564                 return;
27565             }
27566             
27567             this.scale = this.startScale;
27568             
27569             this.onRotateFail();
27570             
27571             return false;
27572         }
27573         
27574         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27575
27576         if(this.isDocument){
27577             this.setThumbBoxSize();
27578             this.setThumbBoxPosition();
27579             this.setCanvasPosition();
27580         }
27581         
27582         this.draw();
27583         
27584         this.fireEvent('rotate', this, 'left');
27585         
27586     },
27587     
27588     onRotateRight : function(e)
27589     {
27590         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27591             
27592             var minScale = this.thumbEl.getWidth() / this.minWidth;
27593         
27594             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27595             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27596             
27597             this.startScale = this.scale;
27598             
27599             while (this.getScaleLevel() < minScale){
27600             
27601                 this.scale = this.scale + 1;
27602                 
27603                 if(!this.zoomable()){
27604                     break;
27605                 }
27606                 
27607                 if(
27608                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27609                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27610                 ){
27611                     continue;
27612                 }
27613                 
27614                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27615
27616                 this.draw();
27617                 
27618                 return;
27619             }
27620             
27621             this.scale = this.startScale;
27622             
27623             this.onRotateFail();
27624             
27625             return false;
27626         }
27627         
27628         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27629
27630         if(this.isDocument){
27631             this.setThumbBoxSize();
27632             this.setThumbBoxPosition();
27633             this.setCanvasPosition();
27634         }
27635         
27636         this.draw();
27637         
27638         this.fireEvent('rotate', this, 'right');
27639     },
27640     
27641     onRotateFail : function()
27642     {
27643         this.errorEl.show(true);
27644         
27645         var _this = this;
27646         
27647         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27648     },
27649     
27650     draw : function()
27651     {
27652         this.previewEl.dom.innerHTML = '';
27653         
27654         var canvasEl = document.createElement("canvas");
27655         
27656         var contextEl = canvasEl.getContext("2d");
27657         
27658         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27659         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27660         var center = this.imageEl.OriginWidth / 2;
27661         
27662         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27663             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27664             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27665             center = this.imageEl.OriginHeight / 2;
27666         }
27667         
27668         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27669         
27670         contextEl.translate(center, center);
27671         contextEl.rotate(this.rotate * Math.PI / 180);
27672
27673         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27674         
27675         this.canvasEl = document.createElement("canvas");
27676         
27677         this.contextEl = this.canvasEl.getContext("2d");
27678         
27679         switch (this.rotate) {
27680             case 0 :
27681                 
27682                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27683                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27684                 
27685                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27686                 
27687                 break;
27688             case 90 : 
27689                 
27690                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27691                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27692                 
27693                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27694                     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);
27695                     break;
27696                 }
27697                 
27698                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27699                 
27700                 break;
27701             case 180 :
27702                 
27703                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27704                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27705                 
27706                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27707                     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);
27708                     break;
27709                 }
27710                 
27711                 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);
27712                 
27713                 break;
27714             case 270 :
27715                 
27716                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27717                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27718         
27719                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27720                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27721                     break;
27722                 }
27723                 
27724                 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);
27725                 
27726                 break;
27727             default : 
27728                 break;
27729         }
27730         
27731         this.previewEl.appendChild(this.canvasEl);
27732         
27733         this.setCanvasPosition();
27734     },
27735     
27736     crop : function()
27737     {
27738         if(!this.canvasLoaded){
27739             return;
27740         }
27741         
27742         var imageCanvas = document.createElement("canvas");
27743         
27744         var imageContext = imageCanvas.getContext("2d");
27745         
27746         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27747         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27748         
27749         var center = imageCanvas.width / 2;
27750         
27751         imageContext.translate(center, center);
27752         
27753         imageContext.rotate(this.rotate * Math.PI / 180);
27754         
27755         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27756         
27757         var canvas = document.createElement("canvas");
27758         
27759         var context = canvas.getContext("2d");
27760                 
27761         canvas.width = this.minWidth;
27762         canvas.height = this.minHeight;
27763
27764         switch (this.rotate) {
27765             case 0 :
27766                 
27767                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27768                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27769                 
27770                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27771                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27772                 
27773                 var targetWidth = this.minWidth - 2 * x;
27774                 var targetHeight = this.minHeight - 2 * y;
27775                 
27776                 var scale = 1;
27777                 
27778                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27779                     scale = targetWidth / width;
27780                 }
27781                 
27782                 if(x > 0 && y == 0){
27783                     scale = targetHeight / height;
27784                 }
27785                 
27786                 if(x > 0 && y > 0){
27787                     scale = targetWidth / width;
27788                     
27789                     if(width < height){
27790                         scale = targetHeight / height;
27791                     }
27792                 }
27793                 
27794                 context.scale(scale, scale);
27795                 
27796                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27797                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27798
27799                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27800                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27801
27802                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27803                 
27804                 break;
27805             case 90 : 
27806                 
27807                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27808                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27809                 
27810                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27811                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27812                 
27813                 var targetWidth = this.minWidth - 2 * x;
27814                 var targetHeight = this.minHeight - 2 * y;
27815                 
27816                 var scale = 1;
27817                 
27818                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27819                     scale = targetWidth / width;
27820                 }
27821                 
27822                 if(x > 0 && y == 0){
27823                     scale = targetHeight / height;
27824                 }
27825                 
27826                 if(x > 0 && y > 0){
27827                     scale = targetWidth / width;
27828                     
27829                     if(width < height){
27830                         scale = targetHeight / height;
27831                     }
27832                 }
27833                 
27834                 context.scale(scale, scale);
27835                 
27836                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27837                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27838
27839                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27840                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27841                 
27842                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27843                 
27844                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27845                 
27846                 break;
27847             case 180 :
27848                 
27849                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27850                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27851                 
27852                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27853                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27854                 
27855                 var targetWidth = this.minWidth - 2 * x;
27856                 var targetHeight = this.minHeight - 2 * y;
27857                 
27858                 var scale = 1;
27859                 
27860                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27861                     scale = targetWidth / width;
27862                 }
27863                 
27864                 if(x > 0 && y == 0){
27865                     scale = targetHeight / height;
27866                 }
27867                 
27868                 if(x > 0 && y > 0){
27869                     scale = targetWidth / width;
27870                     
27871                     if(width < height){
27872                         scale = targetHeight / height;
27873                     }
27874                 }
27875                 
27876                 context.scale(scale, scale);
27877                 
27878                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27879                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27880
27881                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27882                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27883
27884                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27885                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27886                 
27887                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27888                 
27889                 break;
27890             case 270 :
27891                 
27892                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27893                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27894                 
27895                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27896                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27897                 
27898                 var targetWidth = this.minWidth - 2 * x;
27899                 var targetHeight = this.minHeight - 2 * y;
27900                 
27901                 var scale = 1;
27902                 
27903                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27904                     scale = targetWidth / width;
27905                 }
27906                 
27907                 if(x > 0 && y == 0){
27908                     scale = targetHeight / height;
27909                 }
27910                 
27911                 if(x > 0 && y > 0){
27912                     scale = targetWidth / width;
27913                     
27914                     if(width < height){
27915                         scale = targetHeight / height;
27916                     }
27917                 }
27918                 
27919                 context.scale(scale, scale);
27920                 
27921                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27922                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27923
27924                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27925                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27926                 
27927                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27928                 
27929                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27930                 
27931                 break;
27932             default : 
27933                 break;
27934         }
27935         
27936         this.cropData = canvas.toDataURL(this.cropType);
27937         
27938         if(this.fireEvent('crop', this, this.cropData) !== false){
27939             this.process(this.file, this.cropData);
27940         }
27941         
27942         return;
27943         
27944     },
27945     
27946     setThumbBoxSize : function()
27947     {
27948         var width, height;
27949         
27950         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27951             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27952             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27953             
27954             this.minWidth = width;
27955             this.minHeight = height;
27956             
27957             if(this.rotate == 90 || this.rotate == 270){
27958                 this.minWidth = height;
27959                 this.minHeight = width;
27960             }
27961         }
27962         
27963         height = 300;
27964         width = Math.ceil(this.minWidth * height / this.minHeight);
27965         
27966         if(this.minWidth > this.minHeight){
27967             width = 300;
27968             height = Math.ceil(this.minHeight * width / this.minWidth);
27969         }
27970         
27971         this.thumbEl.setStyle({
27972             width : width + 'px',
27973             height : height + 'px'
27974         });
27975
27976         return;
27977             
27978     },
27979     
27980     setThumbBoxPosition : function()
27981     {
27982         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27983         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27984         
27985         this.thumbEl.setLeft(x);
27986         this.thumbEl.setTop(y);
27987         
27988     },
27989     
27990     baseRotateLevel : function()
27991     {
27992         this.baseRotate = 1;
27993         
27994         if(
27995                 typeof(this.exif) != 'undefined' &&
27996                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27997                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27998         ){
27999             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28000         }
28001         
28002         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28003         
28004     },
28005     
28006     baseScaleLevel : function()
28007     {
28008         var width, height;
28009         
28010         if(this.isDocument){
28011             
28012             if(this.baseRotate == 6 || this.baseRotate == 8){
28013             
28014                 height = this.thumbEl.getHeight();
28015                 this.baseScale = height / this.imageEl.OriginWidth;
28016
28017                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28018                     width = this.thumbEl.getWidth();
28019                     this.baseScale = width / this.imageEl.OriginHeight;
28020                 }
28021
28022                 return;
28023             }
28024
28025             height = this.thumbEl.getHeight();
28026             this.baseScale = height / this.imageEl.OriginHeight;
28027
28028             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28029                 width = this.thumbEl.getWidth();
28030                 this.baseScale = width / this.imageEl.OriginWidth;
28031             }
28032
28033             return;
28034         }
28035         
28036         if(this.baseRotate == 6 || this.baseRotate == 8){
28037             
28038             width = this.thumbEl.getHeight();
28039             this.baseScale = width / this.imageEl.OriginHeight;
28040             
28041             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28042                 height = this.thumbEl.getWidth();
28043                 this.baseScale = height / this.imageEl.OriginHeight;
28044             }
28045             
28046             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28047                 height = this.thumbEl.getWidth();
28048                 this.baseScale = height / this.imageEl.OriginHeight;
28049                 
28050                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28051                     width = this.thumbEl.getHeight();
28052                     this.baseScale = width / this.imageEl.OriginWidth;
28053                 }
28054             }
28055             
28056             return;
28057         }
28058         
28059         width = this.thumbEl.getWidth();
28060         this.baseScale = width / this.imageEl.OriginWidth;
28061         
28062         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28063             height = this.thumbEl.getHeight();
28064             this.baseScale = height / this.imageEl.OriginHeight;
28065         }
28066         
28067         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28068             
28069             height = this.thumbEl.getHeight();
28070             this.baseScale = height / this.imageEl.OriginHeight;
28071             
28072             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28073                 width = this.thumbEl.getWidth();
28074                 this.baseScale = width / this.imageEl.OriginWidth;
28075             }
28076             
28077         }
28078         
28079         return;
28080     },
28081     
28082     getScaleLevel : function()
28083     {
28084         return this.baseScale * Math.pow(1.1, this.scale);
28085     },
28086     
28087     onTouchStart : function(e)
28088     {
28089         if(!this.canvasLoaded){
28090             this.beforeSelectFile(e);
28091             return;
28092         }
28093         
28094         var touches = e.browserEvent.touches;
28095         
28096         if(!touches){
28097             return;
28098         }
28099         
28100         if(touches.length == 1){
28101             this.onMouseDown(e);
28102             return;
28103         }
28104         
28105         if(touches.length != 2){
28106             return;
28107         }
28108         
28109         var coords = [];
28110         
28111         for(var i = 0, finger; finger = touches[i]; i++){
28112             coords.push(finger.pageX, finger.pageY);
28113         }
28114         
28115         var x = Math.pow(coords[0] - coords[2], 2);
28116         var y = Math.pow(coords[1] - coords[3], 2);
28117         
28118         this.startDistance = Math.sqrt(x + y);
28119         
28120         this.startScale = this.scale;
28121         
28122         this.pinching = true;
28123         this.dragable = false;
28124         
28125     },
28126     
28127     onTouchMove : function(e)
28128     {
28129         if(!this.pinching && !this.dragable){
28130             return;
28131         }
28132         
28133         var touches = e.browserEvent.touches;
28134         
28135         if(!touches){
28136             return;
28137         }
28138         
28139         if(this.dragable){
28140             this.onMouseMove(e);
28141             return;
28142         }
28143         
28144         var coords = [];
28145         
28146         for(var i = 0, finger; finger = touches[i]; i++){
28147             coords.push(finger.pageX, finger.pageY);
28148         }
28149         
28150         var x = Math.pow(coords[0] - coords[2], 2);
28151         var y = Math.pow(coords[1] - coords[3], 2);
28152         
28153         this.endDistance = Math.sqrt(x + y);
28154         
28155         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28156         
28157         if(!this.zoomable()){
28158             this.scale = this.startScale;
28159             return;
28160         }
28161         
28162         this.draw();
28163         
28164     },
28165     
28166     onTouchEnd : function(e)
28167     {
28168         this.pinching = false;
28169         this.dragable = false;
28170         
28171     },
28172     
28173     process : function(file, crop)
28174     {
28175         if(this.loadMask){
28176             this.maskEl.mask(this.loadingText);
28177         }
28178         
28179         this.xhr = new XMLHttpRequest();
28180         
28181         file.xhr = this.xhr;
28182
28183         this.xhr.open(this.method, this.url, true);
28184         
28185         var headers = {
28186             "Accept": "application/json",
28187             "Cache-Control": "no-cache",
28188             "X-Requested-With": "XMLHttpRequest"
28189         };
28190         
28191         for (var headerName in headers) {
28192             var headerValue = headers[headerName];
28193             if (headerValue) {
28194                 this.xhr.setRequestHeader(headerName, headerValue);
28195             }
28196         }
28197         
28198         var _this = this;
28199         
28200         this.xhr.onload = function()
28201         {
28202             _this.xhrOnLoad(_this.xhr);
28203         }
28204         
28205         this.xhr.onerror = function()
28206         {
28207             _this.xhrOnError(_this.xhr);
28208         }
28209         
28210         var formData = new FormData();
28211
28212         formData.append('returnHTML', 'NO');
28213         
28214         if(crop){
28215             formData.append('crop', crop);
28216         }
28217         
28218         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28219             formData.append(this.paramName, file, file.name);
28220         }
28221         
28222         if(typeof(file.filename) != 'undefined'){
28223             formData.append('filename', file.filename);
28224         }
28225         
28226         if(typeof(file.mimetype) != 'undefined'){
28227             formData.append('mimetype', file.mimetype);
28228         }
28229         
28230         if(this.fireEvent('arrange', this, formData) != false){
28231             this.xhr.send(formData);
28232         };
28233     },
28234     
28235     xhrOnLoad : function(xhr)
28236     {
28237         if(this.loadMask){
28238             this.maskEl.unmask();
28239         }
28240         
28241         if (xhr.readyState !== 4) {
28242             this.fireEvent('exception', this, xhr);
28243             return;
28244         }
28245
28246         var response = Roo.decode(xhr.responseText);
28247         
28248         if(!response.success){
28249             this.fireEvent('exception', this, xhr);
28250             return;
28251         }
28252         
28253         var response = Roo.decode(xhr.responseText);
28254         
28255         this.fireEvent('upload', this, response);
28256         
28257     },
28258     
28259     xhrOnError : function()
28260     {
28261         if(this.loadMask){
28262             this.maskEl.unmask();
28263         }
28264         
28265         Roo.log('xhr on error');
28266         
28267         var response = Roo.decode(xhr.responseText);
28268           
28269         Roo.log(response);
28270         
28271     },
28272     
28273     prepare : function(file)
28274     {   
28275         if(this.loadMask){
28276             this.maskEl.mask(this.loadingText);
28277         }
28278         
28279         this.file = false;
28280         this.exif = {};
28281         
28282         if(typeof(file) === 'string'){
28283             this.loadCanvas(file);
28284             return;
28285         }
28286         
28287         if(!file || !this.urlAPI){
28288             return;
28289         }
28290         
28291         this.file = file;
28292         this.cropType = file.type;
28293         
28294         var _this = this;
28295         
28296         if(this.fireEvent('prepare', this, this.file) != false){
28297             
28298             var reader = new FileReader();
28299             
28300             reader.onload = function (e) {
28301                 if (e.target.error) {
28302                     Roo.log(e.target.error);
28303                     return;
28304                 }
28305                 
28306                 var buffer = e.target.result,
28307                     dataView = new DataView(buffer),
28308                     offset = 2,
28309                     maxOffset = dataView.byteLength - 4,
28310                     markerBytes,
28311                     markerLength;
28312                 
28313                 if (dataView.getUint16(0) === 0xffd8) {
28314                     while (offset < maxOffset) {
28315                         markerBytes = dataView.getUint16(offset);
28316                         
28317                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28318                             markerLength = dataView.getUint16(offset + 2) + 2;
28319                             if (offset + markerLength > dataView.byteLength) {
28320                                 Roo.log('Invalid meta data: Invalid segment size.');
28321                                 break;
28322                             }
28323                             
28324                             if(markerBytes == 0xffe1){
28325                                 _this.parseExifData(
28326                                     dataView,
28327                                     offset,
28328                                     markerLength
28329                                 );
28330                             }
28331                             
28332                             offset += markerLength;
28333                             
28334                             continue;
28335                         }
28336                         
28337                         break;
28338                     }
28339                     
28340                 }
28341                 
28342                 var url = _this.urlAPI.createObjectURL(_this.file);
28343                 
28344                 _this.loadCanvas(url);
28345                 
28346                 return;
28347             }
28348             
28349             reader.readAsArrayBuffer(this.file);
28350             
28351         }
28352         
28353     },
28354     
28355     parseExifData : function(dataView, offset, length)
28356     {
28357         var tiffOffset = offset + 10,
28358             littleEndian,
28359             dirOffset;
28360     
28361         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28362             // No Exif data, might be XMP data instead
28363             return;
28364         }
28365         
28366         // Check for the ASCII code for "Exif" (0x45786966):
28367         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28368             // No Exif data, might be XMP data instead
28369             return;
28370         }
28371         if (tiffOffset + 8 > dataView.byteLength) {
28372             Roo.log('Invalid Exif data: Invalid segment size.');
28373             return;
28374         }
28375         // Check for the two null bytes:
28376         if (dataView.getUint16(offset + 8) !== 0x0000) {
28377             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28378             return;
28379         }
28380         // Check the byte alignment:
28381         switch (dataView.getUint16(tiffOffset)) {
28382         case 0x4949:
28383             littleEndian = true;
28384             break;
28385         case 0x4D4D:
28386             littleEndian = false;
28387             break;
28388         default:
28389             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28390             return;
28391         }
28392         // Check for the TIFF tag marker (0x002A):
28393         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28394             Roo.log('Invalid Exif data: Missing TIFF marker.');
28395             return;
28396         }
28397         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28398         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28399         
28400         this.parseExifTags(
28401             dataView,
28402             tiffOffset,
28403             tiffOffset + dirOffset,
28404             littleEndian
28405         );
28406     },
28407     
28408     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28409     {
28410         var tagsNumber,
28411             dirEndOffset,
28412             i;
28413         if (dirOffset + 6 > dataView.byteLength) {
28414             Roo.log('Invalid Exif data: Invalid directory offset.');
28415             return;
28416         }
28417         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28418         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28419         if (dirEndOffset + 4 > dataView.byteLength) {
28420             Roo.log('Invalid Exif data: Invalid directory size.');
28421             return;
28422         }
28423         for (i = 0; i < tagsNumber; i += 1) {
28424             this.parseExifTag(
28425                 dataView,
28426                 tiffOffset,
28427                 dirOffset + 2 + 12 * i, // tag offset
28428                 littleEndian
28429             );
28430         }
28431         // Return the offset to the next directory:
28432         return dataView.getUint32(dirEndOffset, littleEndian);
28433     },
28434     
28435     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28436     {
28437         var tag = dataView.getUint16(offset, littleEndian);
28438         
28439         this.exif[tag] = this.getExifValue(
28440             dataView,
28441             tiffOffset,
28442             offset,
28443             dataView.getUint16(offset + 2, littleEndian), // tag type
28444             dataView.getUint32(offset + 4, littleEndian), // tag length
28445             littleEndian
28446         );
28447     },
28448     
28449     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28450     {
28451         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28452             tagSize,
28453             dataOffset,
28454             values,
28455             i,
28456             str,
28457             c;
28458     
28459         if (!tagType) {
28460             Roo.log('Invalid Exif data: Invalid tag type.');
28461             return;
28462         }
28463         
28464         tagSize = tagType.size * length;
28465         // Determine if the value is contained in the dataOffset bytes,
28466         // or if the value at the dataOffset is a pointer to the actual data:
28467         dataOffset = tagSize > 4 ?
28468                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28469         if (dataOffset + tagSize > dataView.byteLength) {
28470             Roo.log('Invalid Exif data: Invalid data offset.');
28471             return;
28472         }
28473         if (length === 1) {
28474             return tagType.getValue(dataView, dataOffset, littleEndian);
28475         }
28476         values = [];
28477         for (i = 0; i < length; i += 1) {
28478             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28479         }
28480         
28481         if (tagType.ascii) {
28482             str = '';
28483             // Concatenate the chars:
28484             for (i = 0; i < values.length; i += 1) {
28485                 c = values[i];
28486                 // Ignore the terminating NULL byte(s):
28487                 if (c === '\u0000') {
28488                     break;
28489                 }
28490                 str += c;
28491             }
28492             return str;
28493         }
28494         return values;
28495     }
28496     
28497 });
28498
28499 Roo.apply(Roo.bootstrap.UploadCropbox, {
28500     tags : {
28501         'Orientation': 0x0112
28502     },
28503     
28504     Orientation: {
28505             1: 0, //'top-left',
28506 //            2: 'top-right',
28507             3: 180, //'bottom-right',
28508 //            4: 'bottom-left',
28509 //            5: 'left-top',
28510             6: 90, //'right-top',
28511 //            7: 'right-bottom',
28512             8: 270 //'left-bottom'
28513     },
28514     
28515     exifTagTypes : {
28516         // byte, 8-bit unsigned int:
28517         1: {
28518             getValue: function (dataView, dataOffset) {
28519                 return dataView.getUint8(dataOffset);
28520             },
28521             size: 1
28522         },
28523         // ascii, 8-bit byte:
28524         2: {
28525             getValue: function (dataView, dataOffset) {
28526                 return String.fromCharCode(dataView.getUint8(dataOffset));
28527             },
28528             size: 1,
28529             ascii: true
28530         },
28531         // short, 16 bit int:
28532         3: {
28533             getValue: function (dataView, dataOffset, littleEndian) {
28534                 return dataView.getUint16(dataOffset, littleEndian);
28535             },
28536             size: 2
28537         },
28538         // long, 32 bit int:
28539         4: {
28540             getValue: function (dataView, dataOffset, littleEndian) {
28541                 return dataView.getUint32(dataOffset, littleEndian);
28542             },
28543             size: 4
28544         },
28545         // rational = two long values, first is numerator, second is denominator:
28546         5: {
28547             getValue: function (dataView, dataOffset, littleEndian) {
28548                 return dataView.getUint32(dataOffset, littleEndian) /
28549                     dataView.getUint32(dataOffset + 4, littleEndian);
28550             },
28551             size: 8
28552         },
28553         // slong, 32 bit signed int:
28554         9: {
28555             getValue: function (dataView, dataOffset, littleEndian) {
28556                 return dataView.getInt32(dataOffset, littleEndian);
28557             },
28558             size: 4
28559         },
28560         // srational, two slongs, first is numerator, second is denominator:
28561         10: {
28562             getValue: function (dataView, dataOffset, littleEndian) {
28563                 return dataView.getInt32(dataOffset, littleEndian) /
28564                     dataView.getInt32(dataOffset + 4, littleEndian);
28565             },
28566             size: 8
28567         }
28568     },
28569     
28570     footer : {
28571         STANDARD : [
28572             {
28573                 tag : 'div',
28574                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28575                 action : 'rotate-left',
28576                 cn : [
28577                     {
28578                         tag : 'button',
28579                         cls : 'btn btn-default',
28580                         html : '<i class="fa fa-undo"></i>'
28581                     }
28582                 ]
28583             },
28584             {
28585                 tag : 'div',
28586                 cls : 'btn-group roo-upload-cropbox-picture',
28587                 action : 'picture',
28588                 cn : [
28589                     {
28590                         tag : 'button',
28591                         cls : 'btn btn-default',
28592                         html : '<i class="fa fa-picture-o"></i>'
28593                     }
28594                 ]
28595             },
28596             {
28597                 tag : 'div',
28598                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28599                 action : 'rotate-right',
28600                 cn : [
28601                     {
28602                         tag : 'button',
28603                         cls : 'btn btn-default',
28604                         html : '<i class="fa fa-repeat"></i>'
28605                     }
28606                 ]
28607             }
28608         ],
28609         DOCUMENT : [
28610             {
28611                 tag : 'div',
28612                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28613                 action : 'rotate-left',
28614                 cn : [
28615                     {
28616                         tag : 'button',
28617                         cls : 'btn btn-default',
28618                         html : '<i class="fa fa-undo"></i>'
28619                     }
28620                 ]
28621             },
28622             {
28623                 tag : 'div',
28624                 cls : 'btn-group roo-upload-cropbox-download',
28625                 action : 'download',
28626                 cn : [
28627                     {
28628                         tag : 'button',
28629                         cls : 'btn btn-default',
28630                         html : '<i class="fa fa-download"></i>'
28631                     }
28632                 ]
28633             },
28634             {
28635                 tag : 'div',
28636                 cls : 'btn-group roo-upload-cropbox-crop',
28637                 action : 'crop',
28638                 cn : [
28639                     {
28640                         tag : 'button',
28641                         cls : 'btn btn-default',
28642                         html : '<i class="fa fa-crop"></i>'
28643                     }
28644                 ]
28645             },
28646             {
28647                 tag : 'div',
28648                 cls : 'btn-group roo-upload-cropbox-trash',
28649                 action : 'trash',
28650                 cn : [
28651                     {
28652                         tag : 'button',
28653                         cls : 'btn btn-default',
28654                         html : '<i class="fa fa-trash"></i>'
28655                     }
28656                 ]
28657             },
28658             {
28659                 tag : 'div',
28660                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28661                 action : 'rotate-right',
28662                 cn : [
28663                     {
28664                         tag : 'button',
28665                         cls : 'btn btn-default',
28666                         html : '<i class="fa fa-repeat"></i>'
28667                     }
28668                 ]
28669             }
28670         ],
28671         ROTATOR : [
28672             {
28673                 tag : 'div',
28674                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28675                 action : 'rotate-left',
28676                 cn : [
28677                     {
28678                         tag : 'button',
28679                         cls : 'btn btn-default',
28680                         html : '<i class="fa fa-undo"></i>'
28681                     }
28682                 ]
28683             },
28684             {
28685                 tag : 'div',
28686                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28687                 action : 'rotate-right',
28688                 cn : [
28689                     {
28690                         tag : 'button',
28691                         cls : 'btn btn-default',
28692                         html : '<i class="fa fa-repeat"></i>'
28693                     }
28694                 ]
28695             }
28696         ]
28697     }
28698 });
28699
28700 /*
28701 * Licence: LGPL
28702 */
28703
28704 /**
28705  * @class Roo.bootstrap.DocumentManager
28706  * @extends Roo.bootstrap.Component
28707  * Bootstrap DocumentManager class
28708  * @cfg {String} paramName default 'imageUpload'
28709  * @cfg {String} toolTipName default 'filename'
28710  * @cfg {String} method default POST
28711  * @cfg {String} url action url
28712  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28713  * @cfg {Boolean} multiple multiple upload default true
28714  * @cfg {Number} thumbSize default 300
28715  * @cfg {String} fieldLabel
28716  * @cfg {Number} labelWidth default 4
28717  * @cfg {String} labelAlign (left|top) default left
28718  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28719 * @cfg {Number} labellg set the width of label (1-12)
28720  * @cfg {Number} labelmd set the width of label (1-12)
28721  * @cfg {Number} labelsm set the width of label (1-12)
28722  * @cfg {Number} labelxs set the width of label (1-12)
28723  * 
28724  * @constructor
28725  * Create a new DocumentManager
28726  * @param {Object} config The config object
28727  */
28728
28729 Roo.bootstrap.DocumentManager = function(config){
28730     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28731     
28732     this.files = [];
28733     this.delegates = [];
28734     
28735     this.addEvents({
28736         /**
28737          * @event initial
28738          * Fire when initial the DocumentManager
28739          * @param {Roo.bootstrap.DocumentManager} this
28740          */
28741         "initial" : true,
28742         /**
28743          * @event inspect
28744          * inspect selected file
28745          * @param {Roo.bootstrap.DocumentManager} this
28746          * @param {File} file
28747          */
28748         "inspect" : true,
28749         /**
28750          * @event exception
28751          * Fire when xhr load exception
28752          * @param {Roo.bootstrap.DocumentManager} this
28753          * @param {XMLHttpRequest} xhr
28754          */
28755         "exception" : true,
28756         /**
28757          * @event afterupload
28758          * Fire when xhr load exception
28759          * @param {Roo.bootstrap.DocumentManager} this
28760          * @param {XMLHttpRequest} xhr
28761          */
28762         "afterupload" : true,
28763         /**
28764          * @event prepare
28765          * prepare the form data
28766          * @param {Roo.bootstrap.DocumentManager} this
28767          * @param {Object} formData
28768          */
28769         "prepare" : true,
28770         /**
28771          * @event remove
28772          * Fire when remove the file
28773          * @param {Roo.bootstrap.DocumentManager} this
28774          * @param {Object} file
28775          */
28776         "remove" : true,
28777         /**
28778          * @event refresh
28779          * Fire after refresh the file
28780          * @param {Roo.bootstrap.DocumentManager} this
28781          */
28782         "refresh" : true,
28783         /**
28784          * @event click
28785          * Fire after click the image
28786          * @param {Roo.bootstrap.DocumentManager} this
28787          * @param {Object} file
28788          */
28789         "click" : true,
28790         /**
28791          * @event edit
28792          * Fire when upload a image and editable set to true
28793          * @param {Roo.bootstrap.DocumentManager} this
28794          * @param {Object} file
28795          */
28796         "edit" : true,
28797         /**
28798          * @event beforeselectfile
28799          * Fire before select file
28800          * @param {Roo.bootstrap.DocumentManager} this
28801          */
28802         "beforeselectfile" : true,
28803         /**
28804          * @event process
28805          * Fire before process file
28806          * @param {Roo.bootstrap.DocumentManager} this
28807          * @param {Object} file
28808          */
28809         "process" : true,
28810         /**
28811          * @event previewrendered
28812          * Fire when preview rendered
28813          * @param {Roo.bootstrap.DocumentManager} this
28814          * @param {Object} file
28815          */
28816         "previewrendered" : true,
28817         /**
28818          */
28819         "previewResize" : true
28820         
28821     });
28822 };
28823
28824 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28825     
28826     boxes : 0,
28827     inputName : '',
28828     thumbSize : 300,
28829     multiple : true,
28830     files : false,
28831     method : 'POST',
28832     url : '',
28833     paramName : 'imageUpload',
28834     toolTipName : 'filename',
28835     fieldLabel : '',
28836     labelWidth : 4,
28837     labelAlign : 'left',
28838     editable : true,
28839     delegates : false,
28840     xhr : false, 
28841     
28842     labellg : 0,
28843     labelmd : 0,
28844     labelsm : 0,
28845     labelxs : 0,
28846     
28847     getAutoCreate : function()
28848     {   
28849         var managerWidget = {
28850             tag : 'div',
28851             cls : 'roo-document-manager',
28852             cn : [
28853                 {
28854                     tag : 'input',
28855                     cls : 'roo-document-manager-selector',
28856                     type : 'file'
28857                 },
28858                 {
28859                     tag : 'div',
28860                     cls : 'roo-document-manager-uploader',
28861                     cn : [
28862                         {
28863                             tag : 'div',
28864                             cls : 'roo-document-manager-upload-btn',
28865                             html : '<i class="fa fa-plus"></i>'
28866                         }
28867                     ]
28868                     
28869                 }
28870             ]
28871         };
28872         
28873         var content = [
28874             {
28875                 tag : 'div',
28876                 cls : 'column col-md-12',
28877                 cn : managerWidget
28878             }
28879         ];
28880         
28881         if(this.fieldLabel.length){
28882             
28883             content = [
28884                 {
28885                     tag : 'div',
28886                     cls : 'column col-md-12',
28887                     html : this.fieldLabel
28888                 },
28889                 {
28890                     tag : 'div',
28891                     cls : 'column col-md-12',
28892                     cn : managerWidget
28893                 }
28894             ];
28895
28896             if(this.labelAlign == 'left'){
28897                 content = [
28898                     {
28899                         tag : 'div',
28900                         cls : 'column',
28901                         html : this.fieldLabel
28902                     },
28903                     {
28904                         tag : 'div',
28905                         cls : 'column',
28906                         cn : managerWidget
28907                     }
28908                 ];
28909                 
28910                 if(this.labelWidth > 12){
28911                     content[0].style = "width: " + this.labelWidth + 'px';
28912                 }
28913
28914                 if(this.labelWidth < 13 && this.labelmd == 0){
28915                     this.labelmd = this.labelWidth;
28916                 }
28917
28918                 if(this.labellg > 0){
28919                     content[0].cls += ' col-lg-' + this.labellg;
28920                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28921                 }
28922
28923                 if(this.labelmd > 0){
28924                     content[0].cls += ' col-md-' + this.labelmd;
28925                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28926                 }
28927
28928                 if(this.labelsm > 0){
28929                     content[0].cls += ' col-sm-' + this.labelsm;
28930                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28931                 }
28932
28933                 if(this.labelxs > 0){
28934                     content[0].cls += ' col-xs-' + this.labelxs;
28935                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28936                 }
28937                 
28938             }
28939         }
28940         
28941         var cfg = {
28942             tag : 'div',
28943             cls : 'row clearfix',
28944             cn : content
28945         };
28946         
28947         return cfg;
28948         
28949     },
28950     
28951     initEvents : function()
28952     {
28953         this.managerEl = this.el.select('.roo-document-manager', true).first();
28954         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28955         
28956         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28957         this.selectorEl.hide();
28958         
28959         if(this.multiple){
28960             this.selectorEl.attr('multiple', 'multiple');
28961         }
28962         
28963         this.selectorEl.on('change', this.onFileSelected, this);
28964         
28965         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28966         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28967         
28968         this.uploader.on('click', this.onUploaderClick, this);
28969         
28970         this.renderProgressDialog();
28971         
28972         var _this = this;
28973         
28974         window.addEventListener("resize", function() { _this.refresh(); } );
28975         
28976         this.fireEvent('initial', this);
28977     },
28978     
28979     renderProgressDialog : function()
28980     {
28981         var _this = this;
28982         
28983         this.progressDialog = new Roo.bootstrap.Modal({
28984             cls : 'roo-document-manager-progress-dialog',
28985             allow_close : false,
28986             title : '',
28987             buttons : [
28988                 {
28989                     name  :'cancel',
28990                     weight : 'danger',
28991                     html : 'Cancel'
28992                 }
28993             ], 
28994             listeners : { 
28995                 btnclick : function() {
28996                     _this.uploadCancel();
28997                     this.hide();
28998                 }
28999             }
29000         });
29001          
29002         this.progressDialog.render(Roo.get(document.body));
29003          
29004         this.progress = new Roo.bootstrap.Progress({
29005             cls : 'roo-document-manager-progress',
29006             active : true,
29007             striped : true
29008         });
29009         
29010         this.progress.render(this.progressDialog.getChildContainer());
29011         
29012         this.progressBar = new Roo.bootstrap.ProgressBar({
29013             cls : 'roo-document-manager-progress-bar',
29014             aria_valuenow : 0,
29015             aria_valuemin : 0,
29016             aria_valuemax : 12,
29017             panel : 'success'
29018         });
29019         
29020         this.progressBar.render(this.progress.getChildContainer());
29021     },
29022     
29023     onUploaderClick : function(e)
29024     {
29025         e.preventDefault();
29026      
29027         if(this.fireEvent('beforeselectfile', this) != false){
29028             this.selectorEl.dom.click();
29029         }
29030         
29031     },
29032     
29033     onFileSelected : function(e)
29034     {
29035         e.preventDefault();
29036         
29037         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29038             return;
29039         }
29040         
29041         Roo.each(this.selectorEl.dom.files, function(file){
29042             if(this.fireEvent('inspect', this, file) != false){
29043                 this.files.push(file);
29044             }
29045         }, this);
29046         
29047         this.queue();
29048         
29049     },
29050     
29051     queue : function()
29052     {
29053         this.selectorEl.dom.value = '';
29054         
29055         if(!this.files || !this.files.length){
29056             return;
29057         }
29058         
29059         if(this.boxes > 0 && this.files.length > this.boxes){
29060             this.files = this.files.slice(0, this.boxes);
29061         }
29062         
29063         this.uploader.show();
29064         
29065         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29066             this.uploader.hide();
29067         }
29068         
29069         var _this = this;
29070         
29071         var files = [];
29072         
29073         var docs = [];
29074         
29075         Roo.each(this.files, function(file){
29076             
29077             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29078                 var f = this.renderPreview(file);
29079                 files.push(f);
29080                 return;
29081             }
29082             
29083             if(file.type.indexOf('image') != -1){
29084                 this.delegates.push(
29085                     (function(){
29086                         _this.process(file);
29087                     }).createDelegate(this)
29088                 );
29089         
29090                 return;
29091             }
29092             
29093             docs.push(
29094                 (function(){
29095                     _this.process(file);
29096                 }).createDelegate(this)
29097             );
29098             
29099         }, this);
29100         
29101         this.files = files;
29102         
29103         this.delegates = this.delegates.concat(docs);
29104         
29105         if(!this.delegates.length){
29106             this.refresh();
29107             return;
29108         }
29109         
29110         this.progressBar.aria_valuemax = this.delegates.length;
29111         
29112         this.arrange();
29113         
29114         return;
29115     },
29116     
29117     arrange : function()
29118     {
29119         if(!this.delegates.length){
29120             this.progressDialog.hide();
29121             this.refresh();
29122             return;
29123         }
29124         
29125         var delegate = this.delegates.shift();
29126         
29127         this.progressDialog.show();
29128         
29129         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29130         
29131         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29132         
29133         delegate();
29134     },
29135     
29136     refresh : function()
29137     {
29138         this.uploader.show();
29139         
29140         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29141             this.uploader.hide();
29142         }
29143         
29144         Roo.isTouch ? this.closable(false) : this.closable(true);
29145         
29146         this.fireEvent('refresh', this);
29147     },
29148     
29149     onRemove : function(e, el, o)
29150     {
29151         e.preventDefault();
29152         
29153         this.fireEvent('remove', this, o);
29154         
29155     },
29156     
29157     remove : function(o)
29158     {
29159         var files = [];
29160         
29161         Roo.each(this.files, function(file){
29162             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29163                 files.push(file);
29164                 return;
29165             }
29166
29167             o.target.remove();
29168
29169         }, this);
29170         
29171         this.files = files;
29172         
29173         this.refresh();
29174     },
29175     
29176     clear : function()
29177     {
29178         Roo.each(this.files, function(file){
29179             if(!file.target){
29180                 return;
29181             }
29182             
29183             file.target.remove();
29184
29185         }, this);
29186         
29187         this.files = [];
29188         
29189         this.refresh();
29190     },
29191     
29192     onClick : function(e, el, o)
29193     {
29194         e.preventDefault();
29195         
29196         this.fireEvent('click', this, o);
29197         
29198     },
29199     
29200     closable : function(closable)
29201     {
29202         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29203             
29204             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29205             
29206             if(closable){
29207                 el.show();
29208                 return;
29209             }
29210             
29211             el.hide();
29212             
29213         }, this);
29214     },
29215     
29216     xhrOnLoad : function(xhr)
29217     {
29218         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29219             el.remove();
29220         }, this);
29221         
29222         if (xhr.readyState !== 4) {
29223             this.arrange();
29224             this.fireEvent('exception', this, xhr);
29225             return;
29226         }
29227
29228         var response = Roo.decode(xhr.responseText);
29229         
29230         if(!response.success){
29231             this.arrange();
29232             this.fireEvent('exception', this, xhr);
29233             return;
29234         }
29235         
29236         var file = this.renderPreview(response.data);
29237         
29238         this.files.push(file);
29239         
29240         this.arrange();
29241         
29242         this.fireEvent('afterupload', this, xhr);
29243         
29244     },
29245     
29246     xhrOnError : function(xhr)
29247     {
29248         Roo.log('xhr on error');
29249         
29250         var response = Roo.decode(xhr.responseText);
29251           
29252         Roo.log(response);
29253         
29254         this.arrange();
29255     },
29256     
29257     process : function(file)
29258     {
29259         if(this.fireEvent('process', this, file) !== false){
29260             if(this.editable && file.type.indexOf('image') != -1){
29261                 this.fireEvent('edit', this, file);
29262                 return;
29263             }
29264
29265             this.uploadStart(file, false);
29266
29267             return;
29268         }
29269         
29270     },
29271     
29272     uploadStart : function(file, crop)
29273     {
29274         this.xhr = new XMLHttpRequest();
29275         
29276         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29277             this.arrange();
29278             return;
29279         }
29280         
29281         file.xhr = this.xhr;
29282             
29283         this.managerEl.createChild({
29284             tag : 'div',
29285             cls : 'roo-document-manager-loading',
29286             cn : [
29287                 {
29288                     tag : 'div',
29289                     tooltip : file.name,
29290                     cls : 'roo-document-manager-thumb',
29291                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29292                 }
29293             ]
29294
29295         });
29296
29297         this.xhr.open(this.method, this.url, true);
29298         
29299         var headers = {
29300             "Accept": "application/json",
29301             "Cache-Control": "no-cache",
29302             "X-Requested-With": "XMLHttpRequest"
29303         };
29304         
29305         for (var headerName in headers) {
29306             var headerValue = headers[headerName];
29307             if (headerValue) {
29308                 this.xhr.setRequestHeader(headerName, headerValue);
29309             }
29310         }
29311         
29312         var _this = this;
29313         
29314         this.xhr.onload = function()
29315         {
29316             _this.xhrOnLoad(_this.xhr);
29317         }
29318         
29319         this.xhr.onerror = function()
29320         {
29321             _this.xhrOnError(_this.xhr);
29322         }
29323         
29324         var formData = new FormData();
29325
29326         formData.append('returnHTML', 'NO');
29327         
29328         if(crop){
29329             formData.append('crop', crop);
29330         }
29331         
29332         formData.append(this.paramName, file, file.name);
29333         
29334         var options = {
29335             file : file, 
29336             manually : false
29337         };
29338         
29339         if(this.fireEvent('prepare', this, formData, options) != false){
29340             
29341             if(options.manually){
29342                 return;
29343             }
29344             
29345             this.xhr.send(formData);
29346             return;
29347         };
29348         
29349         this.uploadCancel();
29350     },
29351     
29352     uploadCancel : function()
29353     {
29354         if (this.xhr) {
29355             this.xhr.abort();
29356         }
29357         
29358         this.delegates = [];
29359         
29360         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29361             el.remove();
29362         }, this);
29363         
29364         this.arrange();
29365     },
29366     
29367     renderPreview : function(file)
29368     {
29369         if(typeof(file.target) != 'undefined' && file.target){
29370             return file;
29371         }
29372         
29373         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29374         
29375         var previewEl = this.managerEl.createChild({
29376             tag : 'div',
29377             cls : 'roo-document-manager-preview',
29378             cn : [
29379                 {
29380                     tag : 'div',
29381                     tooltip : file[this.toolTipName],
29382                     cls : 'roo-document-manager-thumb',
29383                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29384                 },
29385                 {
29386                     tag : 'button',
29387                     cls : 'close',
29388                     html : '<i class="fa fa-times-circle"></i>'
29389                 }
29390             ]
29391         });
29392
29393         var close = previewEl.select('button.close', true).first();
29394
29395         close.on('click', this.onRemove, this, file);
29396
29397         file.target = previewEl;
29398
29399         var image = previewEl.select('img', true).first();
29400         
29401         var _this = this;
29402         
29403         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29404         
29405         image.on('click', this.onClick, this, file);
29406         
29407         this.fireEvent('previewrendered', this, file);
29408         
29409         return file;
29410         
29411     },
29412     
29413     onPreviewLoad : function(file, image)
29414     {
29415         if(typeof(file.target) == 'undefined' || !file.target){
29416             return;
29417         }
29418         
29419         var width = image.dom.naturalWidth || image.dom.width;
29420         var height = image.dom.naturalHeight || image.dom.height;
29421         
29422         if(!this.previewResize) {
29423             return;
29424         }
29425         
29426         if(width > height){
29427             file.target.addClass('wide');
29428             return;
29429         }
29430         
29431         file.target.addClass('tall');
29432         return;
29433         
29434     },
29435     
29436     uploadFromSource : function(file, crop)
29437     {
29438         this.xhr = new XMLHttpRequest();
29439         
29440         this.managerEl.createChild({
29441             tag : 'div',
29442             cls : 'roo-document-manager-loading',
29443             cn : [
29444                 {
29445                     tag : 'div',
29446                     tooltip : file.name,
29447                     cls : 'roo-document-manager-thumb',
29448                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29449                 }
29450             ]
29451
29452         });
29453
29454         this.xhr.open(this.method, this.url, true);
29455         
29456         var headers = {
29457             "Accept": "application/json",
29458             "Cache-Control": "no-cache",
29459             "X-Requested-With": "XMLHttpRequest"
29460         };
29461         
29462         for (var headerName in headers) {
29463             var headerValue = headers[headerName];
29464             if (headerValue) {
29465                 this.xhr.setRequestHeader(headerName, headerValue);
29466             }
29467         }
29468         
29469         var _this = this;
29470         
29471         this.xhr.onload = function()
29472         {
29473             _this.xhrOnLoad(_this.xhr);
29474         }
29475         
29476         this.xhr.onerror = function()
29477         {
29478             _this.xhrOnError(_this.xhr);
29479         }
29480         
29481         var formData = new FormData();
29482
29483         formData.append('returnHTML', 'NO');
29484         
29485         formData.append('crop', crop);
29486         
29487         if(typeof(file.filename) != 'undefined'){
29488             formData.append('filename', file.filename);
29489         }
29490         
29491         if(typeof(file.mimetype) != 'undefined'){
29492             formData.append('mimetype', file.mimetype);
29493         }
29494         
29495         Roo.log(formData);
29496         
29497         if(this.fireEvent('prepare', this, formData) != false){
29498             this.xhr.send(formData);
29499         };
29500     }
29501 });
29502
29503 /*
29504 * Licence: LGPL
29505 */
29506
29507 /**
29508  * @class Roo.bootstrap.DocumentViewer
29509  * @extends Roo.bootstrap.Component
29510  * Bootstrap DocumentViewer class
29511  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29512  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29513  * 
29514  * @constructor
29515  * Create a new DocumentViewer
29516  * @param {Object} config The config object
29517  */
29518
29519 Roo.bootstrap.DocumentViewer = function(config){
29520     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29521     
29522     this.addEvents({
29523         /**
29524          * @event initial
29525          * Fire after initEvent
29526          * @param {Roo.bootstrap.DocumentViewer} this
29527          */
29528         "initial" : true,
29529         /**
29530          * @event click
29531          * Fire after click
29532          * @param {Roo.bootstrap.DocumentViewer} this
29533          */
29534         "click" : true,
29535         /**
29536          * @event download
29537          * Fire after download button
29538          * @param {Roo.bootstrap.DocumentViewer} this
29539          */
29540         "download" : true,
29541         /**
29542          * @event trash
29543          * Fire after trash button
29544          * @param {Roo.bootstrap.DocumentViewer} this
29545          */
29546         "trash" : true
29547         
29548     });
29549 };
29550
29551 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29552     
29553     showDownload : true,
29554     
29555     showTrash : true,
29556     
29557     getAutoCreate : function()
29558     {
29559         var cfg = {
29560             tag : 'div',
29561             cls : 'roo-document-viewer',
29562             cn : [
29563                 {
29564                     tag : 'div',
29565                     cls : 'roo-document-viewer-body',
29566                     cn : [
29567                         {
29568                             tag : 'div',
29569                             cls : 'roo-document-viewer-thumb',
29570                             cn : [
29571                                 {
29572                                     tag : 'img',
29573                                     cls : 'roo-document-viewer-image'
29574                                 }
29575                             ]
29576                         }
29577                     ]
29578                 },
29579                 {
29580                     tag : 'div',
29581                     cls : 'roo-document-viewer-footer',
29582                     cn : {
29583                         tag : 'div',
29584                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29585                         cn : [
29586                             {
29587                                 tag : 'div',
29588                                 cls : 'btn-group roo-document-viewer-download',
29589                                 cn : [
29590                                     {
29591                                         tag : 'button',
29592                                         cls : 'btn btn-default',
29593                                         html : '<i class="fa fa-download"></i>'
29594                                     }
29595                                 ]
29596                             },
29597                             {
29598                                 tag : 'div',
29599                                 cls : 'btn-group roo-document-viewer-trash',
29600                                 cn : [
29601                                     {
29602                                         tag : 'button',
29603                                         cls : 'btn btn-default',
29604                                         html : '<i class="fa fa-trash"></i>'
29605                                     }
29606                                 ]
29607                             }
29608                         ]
29609                     }
29610                 }
29611             ]
29612         };
29613         
29614         return cfg;
29615     },
29616     
29617     initEvents : function()
29618     {
29619         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29620         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29621         
29622         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29623         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29624         
29625         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29626         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29627         
29628         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29629         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29630         
29631         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29632         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29633         
29634         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29635         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29636         
29637         this.bodyEl.on('click', this.onClick, this);
29638         this.downloadBtn.on('click', this.onDownload, this);
29639         this.trashBtn.on('click', this.onTrash, this);
29640         
29641         this.downloadBtn.hide();
29642         this.trashBtn.hide();
29643         
29644         if(this.showDownload){
29645             this.downloadBtn.show();
29646         }
29647         
29648         if(this.showTrash){
29649             this.trashBtn.show();
29650         }
29651         
29652         if(!this.showDownload && !this.showTrash) {
29653             this.footerEl.hide();
29654         }
29655         
29656     },
29657     
29658     initial : function()
29659     {
29660         this.fireEvent('initial', this);
29661         
29662     },
29663     
29664     onClick : function(e)
29665     {
29666         e.preventDefault();
29667         
29668         this.fireEvent('click', this);
29669     },
29670     
29671     onDownload : function(e)
29672     {
29673         e.preventDefault();
29674         
29675         this.fireEvent('download', this);
29676     },
29677     
29678     onTrash : function(e)
29679     {
29680         e.preventDefault();
29681         
29682         this.fireEvent('trash', this);
29683     }
29684     
29685 });
29686 /*
29687  * - LGPL
29688  *
29689  * nav progress bar
29690  * 
29691  */
29692
29693 /**
29694  * @class Roo.bootstrap.NavProgressBar
29695  * @extends Roo.bootstrap.Component
29696  * Bootstrap NavProgressBar class
29697  * 
29698  * @constructor
29699  * Create a new nav progress bar
29700  * @param {Object} config The config object
29701  */
29702
29703 Roo.bootstrap.NavProgressBar = function(config){
29704     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29705
29706     this.bullets = this.bullets || [];
29707    
29708 //    Roo.bootstrap.NavProgressBar.register(this);
29709      this.addEvents({
29710         /**
29711              * @event changed
29712              * Fires when the active item changes
29713              * @param {Roo.bootstrap.NavProgressBar} this
29714              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29715              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29716          */
29717         'changed': true
29718      });
29719     
29720 };
29721
29722 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29723     
29724     bullets : [],
29725     barItems : [],
29726     
29727     getAutoCreate : function()
29728     {
29729         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29730         
29731         cfg = {
29732             tag : 'div',
29733             cls : 'roo-navigation-bar-group',
29734             cn : [
29735                 {
29736                     tag : 'div',
29737                     cls : 'roo-navigation-top-bar'
29738                 },
29739                 {
29740                     tag : 'div',
29741                     cls : 'roo-navigation-bullets-bar',
29742                     cn : [
29743                         {
29744                             tag : 'ul',
29745                             cls : 'roo-navigation-bar'
29746                         }
29747                     ]
29748                 },
29749                 
29750                 {
29751                     tag : 'div',
29752                     cls : 'roo-navigation-bottom-bar'
29753                 }
29754             ]
29755             
29756         };
29757         
29758         return cfg;
29759         
29760     },
29761     
29762     initEvents: function() 
29763     {
29764         
29765     },
29766     
29767     onRender : function(ct, position) 
29768     {
29769         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29770         
29771         if(this.bullets.length){
29772             Roo.each(this.bullets, function(b){
29773                this.addItem(b);
29774             }, this);
29775         }
29776         
29777         this.format();
29778         
29779     },
29780     
29781     addItem : function(cfg)
29782     {
29783         var item = new Roo.bootstrap.NavProgressItem(cfg);
29784         
29785         item.parentId = this.id;
29786         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29787         
29788         if(cfg.html){
29789             var top = new Roo.bootstrap.Element({
29790                 tag : 'div',
29791                 cls : 'roo-navigation-bar-text'
29792             });
29793             
29794             var bottom = new Roo.bootstrap.Element({
29795                 tag : 'div',
29796                 cls : 'roo-navigation-bar-text'
29797             });
29798             
29799             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29800             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29801             
29802             var topText = new Roo.bootstrap.Element({
29803                 tag : 'span',
29804                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29805             });
29806             
29807             var bottomText = new Roo.bootstrap.Element({
29808                 tag : 'span',
29809                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29810             });
29811             
29812             topText.onRender(top.el, null);
29813             bottomText.onRender(bottom.el, null);
29814             
29815             item.topEl = top;
29816             item.bottomEl = bottom;
29817         }
29818         
29819         this.barItems.push(item);
29820         
29821         return item;
29822     },
29823     
29824     getActive : function()
29825     {
29826         var active = false;
29827         
29828         Roo.each(this.barItems, function(v){
29829             
29830             if (!v.isActive()) {
29831                 return;
29832             }
29833             
29834             active = v;
29835             return false;
29836             
29837         });
29838         
29839         return active;
29840     },
29841     
29842     setActiveItem : function(item)
29843     {
29844         var prev = false;
29845         
29846         Roo.each(this.barItems, function(v){
29847             if (v.rid == item.rid) {
29848                 return ;
29849             }
29850             
29851             if (v.isActive()) {
29852                 v.setActive(false);
29853                 prev = v;
29854             }
29855         });
29856
29857         item.setActive(true);
29858         
29859         this.fireEvent('changed', this, item, prev);
29860     },
29861     
29862     getBarItem: function(rid)
29863     {
29864         var ret = false;
29865         
29866         Roo.each(this.barItems, function(e) {
29867             if (e.rid != rid) {
29868                 return;
29869             }
29870             
29871             ret =  e;
29872             return false;
29873         });
29874         
29875         return ret;
29876     },
29877     
29878     indexOfItem : function(item)
29879     {
29880         var index = false;
29881         
29882         Roo.each(this.barItems, function(v, i){
29883             
29884             if (v.rid != item.rid) {
29885                 return;
29886             }
29887             
29888             index = i;
29889             return false
29890         });
29891         
29892         return index;
29893     },
29894     
29895     setActiveNext : function()
29896     {
29897         var i = this.indexOfItem(this.getActive());
29898         
29899         if (i > this.barItems.length) {
29900             return;
29901         }
29902         
29903         this.setActiveItem(this.barItems[i+1]);
29904     },
29905     
29906     setActivePrev : function()
29907     {
29908         var i = this.indexOfItem(this.getActive());
29909         
29910         if (i  < 1) {
29911             return;
29912         }
29913         
29914         this.setActiveItem(this.barItems[i-1]);
29915     },
29916     
29917     format : function()
29918     {
29919         if(!this.barItems.length){
29920             return;
29921         }
29922      
29923         var width = 100 / this.barItems.length;
29924         
29925         Roo.each(this.barItems, function(i){
29926             i.el.setStyle('width', width + '%');
29927             i.topEl.el.setStyle('width', width + '%');
29928             i.bottomEl.el.setStyle('width', width + '%');
29929         }, this);
29930         
29931     }
29932     
29933 });
29934 /*
29935  * - LGPL
29936  *
29937  * Nav Progress Item
29938  * 
29939  */
29940
29941 /**
29942  * @class Roo.bootstrap.NavProgressItem
29943  * @extends Roo.bootstrap.Component
29944  * Bootstrap NavProgressItem class
29945  * @cfg {String} rid the reference id
29946  * @cfg {Boolean} active (true|false) Is item active default false
29947  * @cfg {Boolean} disabled (true|false) Is item active default false
29948  * @cfg {String} html
29949  * @cfg {String} position (top|bottom) text position default bottom
29950  * @cfg {String} icon show icon instead of number
29951  * 
29952  * @constructor
29953  * Create a new NavProgressItem
29954  * @param {Object} config The config object
29955  */
29956 Roo.bootstrap.NavProgressItem = function(config){
29957     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29958     this.addEvents({
29959         // raw events
29960         /**
29961          * @event click
29962          * The raw click event for the entire grid.
29963          * @param {Roo.bootstrap.NavProgressItem} this
29964          * @param {Roo.EventObject} e
29965          */
29966         "click" : true
29967     });
29968    
29969 };
29970
29971 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29972     
29973     rid : '',
29974     active : false,
29975     disabled : false,
29976     html : '',
29977     position : 'bottom',
29978     icon : false,
29979     
29980     getAutoCreate : function()
29981     {
29982         var iconCls = 'roo-navigation-bar-item-icon';
29983         
29984         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29985         
29986         var cfg = {
29987             tag: 'li',
29988             cls: 'roo-navigation-bar-item',
29989             cn : [
29990                 {
29991                     tag : 'i',
29992                     cls : iconCls
29993                 }
29994             ]
29995         };
29996         
29997         if(this.active){
29998             cfg.cls += ' active';
29999         }
30000         if(this.disabled){
30001             cfg.cls += ' disabled';
30002         }
30003         
30004         return cfg;
30005     },
30006     
30007     disable : function()
30008     {
30009         this.setDisabled(true);
30010     },
30011     
30012     enable : function()
30013     {
30014         this.setDisabled(false);
30015     },
30016     
30017     initEvents: function() 
30018     {
30019         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30020         
30021         this.iconEl.on('click', this.onClick, this);
30022     },
30023     
30024     onClick : function(e)
30025     {
30026         e.preventDefault();
30027         
30028         if(this.disabled){
30029             return;
30030         }
30031         
30032         if(this.fireEvent('click', this, e) === false){
30033             return;
30034         };
30035         
30036         this.parent().setActiveItem(this);
30037     },
30038     
30039     isActive: function () 
30040     {
30041         return this.active;
30042     },
30043     
30044     setActive : function(state)
30045     {
30046         if(this.active == state){
30047             return;
30048         }
30049         
30050         this.active = state;
30051         
30052         if (state) {
30053             this.el.addClass('active');
30054             return;
30055         }
30056         
30057         this.el.removeClass('active');
30058         
30059         return;
30060     },
30061     
30062     setDisabled : function(state)
30063     {
30064         if(this.disabled == state){
30065             return;
30066         }
30067         
30068         this.disabled = state;
30069         
30070         if (state) {
30071             this.el.addClass('disabled');
30072             return;
30073         }
30074         
30075         this.el.removeClass('disabled');
30076     },
30077     
30078     tooltipEl : function()
30079     {
30080         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30081     }
30082 });
30083  
30084
30085  /*
30086  * - LGPL
30087  *
30088  * FieldLabel
30089  * 
30090  */
30091
30092 /**
30093  * @class Roo.bootstrap.FieldLabel
30094  * @extends Roo.bootstrap.Component
30095  * Bootstrap FieldLabel class
30096  * @cfg {String} html contents of the element
30097  * @cfg {String} tag tag of the element default label
30098  * @cfg {String} cls class of the element
30099  * @cfg {String} target label target 
30100  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30101  * @cfg {String} invalidClass default "text-warning"
30102  * @cfg {String} validClass default "text-success"
30103  * @cfg {String} iconTooltip default "This field is required"
30104  * @cfg {String} indicatorpos (left|right) default left
30105  * 
30106  * @constructor
30107  * Create a new FieldLabel
30108  * @param {Object} config The config object
30109  */
30110
30111 Roo.bootstrap.FieldLabel = function(config){
30112     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30113     
30114     this.addEvents({
30115             /**
30116              * @event invalid
30117              * Fires after the field has been marked as invalid.
30118              * @param {Roo.form.FieldLabel} this
30119              * @param {String} msg The validation message
30120              */
30121             invalid : true,
30122             /**
30123              * @event valid
30124              * Fires after the field has been validated with no errors.
30125              * @param {Roo.form.FieldLabel} this
30126              */
30127             valid : true
30128         });
30129 };
30130
30131 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30132     
30133     tag: 'label',
30134     cls: '',
30135     html: '',
30136     target: '',
30137     allowBlank : true,
30138     invalidClass : 'has-warning',
30139     validClass : 'has-success',
30140     iconTooltip : 'This field is required',
30141     indicatorpos : 'left',
30142     
30143     getAutoCreate : function(){
30144         
30145         var cls = "";
30146         if (!this.allowBlank) {
30147             cls  = "visible";
30148         }
30149         
30150         var cfg = {
30151             tag : this.tag,
30152             cls : 'roo-bootstrap-field-label ' + this.cls,
30153             for : this.target,
30154             cn : [
30155                 {
30156                     tag : 'i',
30157                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30158                     tooltip : this.iconTooltip
30159                 },
30160                 {
30161                     tag : 'span',
30162                     html : this.html
30163                 }
30164             ] 
30165         };
30166         
30167         if(this.indicatorpos == 'right'){
30168             var cfg = {
30169                 tag : this.tag,
30170                 cls : 'roo-bootstrap-field-label ' + this.cls,
30171                 for : this.target,
30172                 cn : [
30173                     {
30174                         tag : 'span',
30175                         html : this.html
30176                     },
30177                     {
30178                         tag : 'i',
30179                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30180                         tooltip : this.iconTooltip
30181                     }
30182                 ] 
30183             };
30184         }
30185         
30186         return cfg;
30187     },
30188     
30189     initEvents: function() 
30190     {
30191         Roo.bootstrap.Element.superclass.initEvents.call(this);
30192         
30193         this.indicator = this.indicatorEl();
30194         
30195         if(this.indicator){
30196             this.indicator.removeClass('visible');
30197             this.indicator.addClass('invisible');
30198         }
30199         
30200         Roo.bootstrap.FieldLabel.register(this);
30201     },
30202     
30203     indicatorEl : function()
30204     {
30205         var indicator = this.el.select('i.roo-required-indicator',true).first();
30206         
30207         if(!indicator){
30208             return false;
30209         }
30210         
30211         return indicator;
30212         
30213     },
30214     
30215     /**
30216      * Mark this field as valid
30217      */
30218     markValid : function()
30219     {
30220         if(this.indicator){
30221             this.indicator.removeClass('visible');
30222             this.indicator.addClass('invisible');
30223         }
30224         
30225         this.el.removeClass(this.invalidClass);
30226         
30227         this.el.addClass(this.validClass);
30228         
30229         this.fireEvent('valid', this);
30230     },
30231     
30232     /**
30233      * Mark this field as invalid
30234      * @param {String} msg The validation message
30235      */
30236     markInvalid : function(msg)
30237     {
30238         if(this.indicator){
30239             this.indicator.removeClass('invisible');
30240             this.indicator.addClass('visible');
30241         }
30242         
30243         this.el.removeClass(this.validClass);
30244         
30245         this.el.addClass(this.invalidClass);
30246         
30247         this.fireEvent('invalid', this, msg);
30248     }
30249     
30250    
30251 });
30252
30253 Roo.apply(Roo.bootstrap.FieldLabel, {
30254     
30255     groups: {},
30256     
30257      /**
30258     * register a FieldLabel Group
30259     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30260     */
30261     register : function(label)
30262     {
30263         if(this.groups.hasOwnProperty(label.target)){
30264             return;
30265         }
30266      
30267         this.groups[label.target] = label;
30268         
30269     },
30270     /**
30271     * fetch a FieldLabel Group based on the target
30272     * @param {string} target
30273     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30274     */
30275     get: function(target) {
30276         if (typeof(this.groups[target]) == 'undefined') {
30277             return false;
30278         }
30279         
30280         return this.groups[target] ;
30281     }
30282 });
30283
30284  
30285
30286  /*
30287  * - LGPL
30288  *
30289  * page DateSplitField.
30290  * 
30291  */
30292
30293
30294 /**
30295  * @class Roo.bootstrap.DateSplitField
30296  * @extends Roo.bootstrap.Component
30297  * Bootstrap DateSplitField class
30298  * @cfg {string} fieldLabel - the label associated
30299  * @cfg {Number} labelWidth set the width of label (0-12)
30300  * @cfg {String} labelAlign (top|left)
30301  * @cfg {Boolean} dayAllowBlank (true|false) default false
30302  * @cfg {Boolean} monthAllowBlank (true|false) default false
30303  * @cfg {Boolean} yearAllowBlank (true|false) default false
30304  * @cfg {string} dayPlaceholder 
30305  * @cfg {string} monthPlaceholder
30306  * @cfg {string} yearPlaceholder
30307  * @cfg {string} dayFormat default 'd'
30308  * @cfg {string} monthFormat default 'm'
30309  * @cfg {string} yearFormat default 'Y'
30310  * @cfg {Number} labellg set the width of label (1-12)
30311  * @cfg {Number} labelmd set the width of label (1-12)
30312  * @cfg {Number} labelsm set the width of label (1-12)
30313  * @cfg {Number} labelxs set the width of label (1-12)
30314
30315  *     
30316  * @constructor
30317  * Create a new DateSplitField
30318  * @param {Object} config The config object
30319  */
30320
30321 Roo.bootstrap.DateSplitField = function(config){
30322     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30323     
30324     this.addEvents({
30325         // raw events
30326          /**
30327          * @event years
30328          * getting the data of years
30329          * @param {Roo.bootstrap.DateSplitField} this
30330          * @param {Object} years
30331          */
30332         "years" : true,
30333         /**
30334          * @event days
30335          * getting the data of days
30336          * @param {Roo.bootstrap.DateSplitField} this
30337          * @param {Object} days
30338          */
30339         "days" : true,
30340         /**
30341          * @event invalid
30342          * Fires after the field has been marked as invalid.
30343          * @param {Roo.form.Field} this
30344          * @param {String} msg The validation message
30345          */
30346         invalid : true,
30347        /**
30348          * @event valid
30349          * Fires after the field has been validated with no errors.
30350          * @param {Roo.form.Field} this
30351          */
30352         valid : true
30353     });
30354 };
30355
30356 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30357     
30358     fieldLabel : '',
30359     labelAlign : 'top',
30360     labelWidth : 3,
30361     dayAllowBlank : false,
30362     monthAllowBlank : false,
30363     yearAllowBlank : false,
30364     dayPlaceholder : '',
30365     monthPlaceholder : '',
30366     yearPlaceholder : '',
30367     dayFormat : 'd',
30368     monthFormat : 'm',
30369     yearFormat : 'Y',
30370     isFormField : true,
30371     labellg : 0,
30372     labelmd : 0,
30373     labelsm : 0,
30374     labelxs : 0,
30375     
30376     getAutoCreate : function()
30377     {
30378         var cfg = {
30379             tag : 'div',
30380             cls : 'row roo-date-split-field-group',
30381             cn : [
30382                 {
30383                     tag : 'input',
30384                     type : 'hidden',
30385                     cls : 'form-hidden-field roo-date-split-field-group-value',
30386                     name : this.name
30387                 }
30388             ]
30389         };
30390         
30391         var labelCls = 'col-md-12';
30392         var contentCls = 'col-md-4';
30393         
30394         if(this.fieldLabel){
30395             
30396             var label = {
30397                 tag : 'div',
30398                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30399                 cn : [
30400                     {
30401                         tag : 'label',
30402                         html : this.fieldLabel
30403                     }
30404                 ]
30405             };
30406             
30407             if(this.labelAlign == 'left'){
30408             
30409                 if(this.labelWidth > 12){
30410                     label.style = "width: " + this.labelWidth + 'px';
30411                 }
30412
30413                 if(this.labelWidth < 13 && this.labelmd == 0){
30414                     this.labelmd = this.labelWidth;
30415                 }
30416
30417                 if(this.labellg > 0){
30418                     labelCls = ' col-lg-' + this.labellg;
30419                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30420                 }
30421
30422                 if(this.labelmd > 0){
30423                     labelCls = ' col-md-' + this.labelmd;
30424                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30425                 }
30426
30427                 if(this.labelsm > 0){
30428                     labelCls = ' col-sm-' + this.labelsm;
30429                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30430                 }
30431
30432                 if(this.labelxs > 0){
30433                     labelCls = ' col-xs-' + this.labelxs;
30434                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30435                 }
30436             }
30437             
30438             label.cls += ' ' + labelCls;
30439             
30440             cfg.cn.push(label);
30441         }
30442         
30443         Roo.each(['day', 'month', 'year'], function(t){
30444             cfg.cn.push({
30445                 tag : 'div',
30446                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30447             });
30448         }, this);
30449         
30450         return cfg;
30451     },
30452     
30453     inputEl: function ()
30454     {
30455         return this.el.select('.roo-date-split-field-group-value', true).first();
30456     },
30457     
30458     onRender : function(ct, position) 
30459     {
30460         var _this = this;
30461         
30462         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30463         
30464         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30465         
30466         this.dayField = new Roo.bootstrap.ComboBox({
30467             allowBlank : this.dayAllowBlank,
30468             alwaysQuery : true,
30469             displayField : 'value',
30470             editable : false,
30471             fieldLabel : '',
30472             forceSelection : true,
30473             mode : 'local',
30474             placeholder : this.dayPlaceholder,
30475             selectOnFocus : true,
30476             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30477             triggerAction : 'all',
30478             typeAhead : true,
30479             valueField : 'value',
30480             store : new Roo.data.SimpleStore({
30481                 data : (function() {    
30482                     var days = [];
30483                     _this.fireEvent('days', _this, days);
30484                     return days;
30485                 })(),
30486                 fields : [ 'value' ]
30487             }),
30488             listeners : {
30489                 select : function (_self, record, index)
30490                 {
30491                     _this.setValue(_this.getValue());
30492                 }
30493             }
30494         });
30495
30496         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30497         
30498         this.monthField = new Roo.bootstrap.MonthField({
30499             after : '<i class=\"fa fa-calendar\"></i>',
30500             allowBlank : this.monthAllowBlank,
30501             placeholder : this.monthPlaceholder,
30502             readOnly : true,
30503             listeners : {
30504                 render : function (_self)
30505                 {
30506                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30507                         e.preventDefault();
30508                         _self.focus();
30509                     });
30510                 },
30511                 select : function (_self, oldvalue, newvalue)
30512                 {
30513                     _this.setValue(_this.getValue());
30514                 }
30515             }
30516         });
30517         
30518         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30519         
30520         this.yearField = new Roo.bootstrap.ComboBox({
30521             allowBlank : this.yearAllowBlank,
30522             alwaysQuery : true,
30523             displayField : 'value',
30524             editable : false,
30525             fieldLabel : '',
30526             forceSelection : true,
30527             mode : 'local',
30528             placeholder : this.yearPlaceholder,
30529             selectOnFocus : true,
30530             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30531             triggerAction : 'all',
30532             typeAhead : true,
30533             valueField : 'value',
30534             store : new Roo.data.SimpleStore({
30535                 data : (function() {
30536                     var years = [];
30537                     _this.fireEvent('years', _this, years);
30538                     return years;
30539                 })(),
30540                 fields : [ 'value' ]
30541             }),
30542             listeners : {
30543                 select : function (_self, record, index)
30544                 {
30545                     _this.setValue(_this.getValue());
30546                 }
30547             }
30548         });
30549
30550         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30551     },
30552     
30553     setValue : function(v, format)
30554     {
30555         this.inputEl.dom.value = v;
30556         
30557         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30558         
30559         var d = Date.parseDate(v, f);
30560         
30561         if(!d){
30562             this.validate();
30563             return;
30564         }
30565         
30566         this.setDay(d.format(this.dayFormat));
30567         this.setMonth(d.format(this.monthFormat));
30568         this.setYear(d.format(this.yearFormat));
30569         
30570         this.validate();
30571         
30572         return;
30573     },
30574     
30575     setDay : function(v)
30576     {
30577         this.dayField.setValue(v);
30578         this.inputEl.dom.value = this.getValue();
30579         this.validate();
30580         return;
30581     },
30582     
30583     setMonth : function(v)
30584     {
30585         this.monthField.setValue(v, true);
30586         this.inputEl.dom.value = this.getValue();
30587         this.validate();
30588         return;
30589     },
30590     
30591     setYear : function(v)
30592     {
30593         this.yearField.setValue(v);
30594         this.inputEl.dom.value = this.getValue();
30595         this.validate();
30596         return;
30597     },
30598     
30599     getDay : function()
30600     {
30601         return this.dayField.getValue();
30602     },
30603     
30604     getMonth : function()
30605     {
30606         return this.monthField.getValue();
30607     },
30608     
30609     getYear : function()
30610     {
30611         return this.yearField.getValue();
30612     },
30613     
30614     getValue : function()
30615     {
30616         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30617         
30618         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30619         
30620         return date;
30621     },
30622     
30623     reset : function()
30624     {
30625         this.setDay('');
30626         this.setMonth('');
30627         this.setYear('');
30628         this.inputEl.dom.value = '';
30629         this.validate();
30630         return;
30631     },
30632     
30633     validate : function()
30634     {
30635         var d = this.dayField.validate();
30636         var m = this.monthField.validate();
30637         var y = this.yearField.validate();
30638         
30639         var valid = true;
30640         
30641         if(
30642                 (!this.dayAllowBlank && !d) ||
30643                 (!this.monthAllowBlank && !m) ||
30644                 (!this.yearAllowBlank && !y)
30645         ){
30646             valid = false;
30647         }
30648         
30649         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30650             return valid;
30651         }
30652         
30653         if(valid){
30654             this.markValid();
30655             return valid;
30656         }
30657         
30658         this.markInvalid();
30659         
30660         return valid;
30661     },
30662     
30663     markValid : function()
30664     {
30665         
30666         var label = this.el.select('label', true).first();
30667         var icon = this.el.select('i.fa-star', true).first();
30668
30669         if(label && icon){
30670             icon.remove();
30671         }
30672         
30673         this.fireEvent('valid', this);
30674     },
30675     
30676      /**
30677      * Mark this field as invalid
30678      * @param {String} msg The validation message
30679      */
30680     markInvalid : function(msg)
30681     {
30682         
30683         var label = this.el.select('label', true).first();
30684         var icon = this.el.select('i.fa-star', true).first();
30685
30686         if(label && !icon){
30687             this.el.select('.roo-date-split-field-label', true).createChild({
30688                 tag : 'i',
30689                 cls : 'text-danger fa fa-lg fa-star',
30690                 tooltip : 'This field is required',
30691                 style : 'margin-right:5px;'
30692             }, label, true);
30693         }
30694         
30695         this.fireEvent('invalid', this, msg);
30696     },
30697     
30698     clearInvalid : function()
30699     {
30700         var label = this.el.select('label', true).first();
30701         var icon = this.el.select('i.fa-star', true).first();
30702
30703         if(label && icon){
30704             icon.remove();
30705         }
30706         
30707         this.fireEvent('valid', this);
30708     },
30709     
30710     getName: function()
30711     {
30712         return this.name;
30713     }
30714     
30715 });
30716
30717  /**
30718  *
30719  * This is based on 
30720  * http://masonry.desandro.com
30721  *
30722  * The idea is to render all the bricks based on vertical width...
30723  *
30724  * The original code extends 'outlayer' - we might need to use that....
30725  * 
30726  */
30727
30728
30729 /**
30730  * @class Roo.bootstrap.LayoutMasonry
30731  * @extends Roo.bootstrap.Component
30732  * Bootstrap Layout Masonry class
30733  * 
30734  * @constructor
30735  * Create a new Element
30736  * @param {Object} config The config object
30737  */
30738
30739 Roo.bootstrap.LayoutMasonry = function(config){
30740     
30741     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30742     
30743     this.bricks = [];
30744     
30745     Roo.bootstrap.LayoutMasonry.register(this);
30746     
30747     this.addEvents({
30748         // raw events
30749         /**
30750          * @event layout
30751          * Fire after layout the items
30752          * @param {Roo.bootstrap.LayoutMasonry} this
30753          * @param {Roo.EventObject} e
30754          */
30755         "layout" : true
30756     });
30757     
30758 };
30759
30760 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30761     
30762     /**
30763      * @cfg {Boolean} isLayoutInstant = no animation?
30764      */   
30765     isLayoutInstant : false, // needed?
30766    
30767     /**
30768      * @cfg {Number} boxWidth  width of the columns
30769      */   
30770     boxWidth : 450,
30771     
30772       /**
30773      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30774      */   
30775     boxHeight : 0,
30776     
30777     /**
30778      * @cfg {Number} padWidth padding below box..
30779      */   
30780     padWidth : 10, 
30781     
30782     /**
30783      * @cfg {Number} gutter gutter width..
30784      */   
30785     gutter : 10,
30786     
30787      /**
30788      * @cfg {Number} maxCols maximum number of columns
30789      */   
30790     
30791     maxCols: 0,
30792     
30793     /**
30794      * @cfg {Boolean} isAutoInitial defalut true
30795      */   
30796     isAutoInitial : true, 
30797     
30798     containerWidth: 0,
30799     
30800     /**
30801      * @cfg {Boolean} isHorizontal defalut false
30802      */   
30803     isHorizontal : false, 
30804
30805     currentSize : null,
30806     
30807     tag: 'div',
30808     
30809     cls: '',
30810     
30811     bricks: null, //CompositeElement
30812     
30813     cols : 1,
30814     
30815     _isLayoutInited : false,
30816     
30817 //    isAlternative : false, // only use for vertical layout...
30818     
30819     /**
30820      * @cfg {Number} alternativePadWidth padding below box..
30821      */   
30822     alternativePadWidth : 50,
30823     
30824     selectedBrick : [],
30825     
30826     getAutoCreate : function(){
30827         
30828         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30829         
30830         var cfg = {
30831             tag: this.tag,
30832             cls: 'blog-masonary-wrapper ' + this.cls,
30833             cn : {
30834                 cls : 'mas-boxes masonary'
30835             }
30836         };
30837         
30838         return cfg;
30839     },
30840     
30841     getChildContainer: function( )
30842     {
30843         if (this.boxesEl) {
30844             return this.boxesEl;
30845         }
30846         
30847         this.boxesEl = this.el.select('.mas-boxes').first();
30848         
30849         return this.boxesEl;
30850     },
30851     
30852     
30853     initEvents : function()
30854     {
30855         var _this = this;
30856         
30857         if(this.isAutoInitial){
30858             Roo.log('hook children rendered');
30859             this.on('childrenrendered', function() {
30860                 Roo.log('children rendered');
30861                 _this.initial();
30862             } ,this);
30863         }
30864     },
30865     
30866     initial : function()
30867     {
30868         this.selectedBrick = [];
30869         
30870         this.currentSize = this.el.getBox(true);
30871         
30872         Roo.EventManager.onWindowResize(this.resize, this); 
30873
30874         if(!this.isAutoInitial){
30875             this.layout();
30876             return;
30877         }
30878         
30879         this.layout();
30880         
30881         return;
30882         //this.layout.defer(500,this);
30883         
30884     },
30885     
30886     resize : function()
30887     {
30888         var cs = this.el.getBox(true);
30889         
30890         if (
30891                 this.currentSize.width == cs.width && 
30892                 this.currentSize.x == cs.x && 
30893                 this.currentSize.height == cs.height && 
30894                 this.currentSize.y == cs.y 
30895         ) {
30896             Roo.log("no change in with or X or Y");
30897             return;
30898         }
30899         
30900         this.currentSize = cs;
30901         
30902         this.layout();
30903         
30904     },
30905     
30906     layout : function()
30907     {   
30908         this._resetLayout();
30909         
30910         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30911         
30912         this.layoutItems( isInstant );
30913       
30914         this._isLayoutInited = true;
30915         
30916         this.fireEvent('layout', this);
30917         
30918     },
30919     
30920     _resetLayout : function()
30921     {
30922         if(this.isHorizontal){
30923             this.horizontalMeasureColumns();
30924             return;
30925         }
30926         
30927         this.verticalMeasureColumns();
30928         
30929     },
30930     
30931     verticalMeasureColumns : function()
30932     {
30933         this.getContainerWidth();
30934         
30935 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30936 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30937 //            return;
30938 //        }
30939         
30940         var boxWidth = this.boxWidth + this.padWidth;
30941         
30942         if(this.containerWidth < this.boxWidth){
30943             boxWidth = this.containerWidth
30944         }
30945         
30946         var containerWidth = this.containerWidth;
30947         
30948         var cols = Math.floor(containerWidth / boxWidth);
30949         
30950         this.cols = Math.max( cols, 1 );
30951         
30952         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30953         
30954         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30955         
30956         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30957         
30958         this.colWidth = boxWidth + avail - this.padWidth;
30959         
30960         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30961         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30962     },
30963     
30964     horizontalMeasureColumns : function()
30965     {
30966         this.getContainerWidth();
30967         
30968         var boxWidth = this.boxWidth;
30969         
30970         if(this.containerWidth < boxWidth){
30971             boxWidth = this.containerWidth;
30972         }
30973         
30974         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30975         
30976         this.el.setHeight(boxWidth);
30977         
30978     },
30979     
30980     getContainerWidth : function()
30981     {
30982         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30983     },
30984     
30985     layoutItems : function( isInstant )
30986     {
30987         Roo.log(this.bricks);
30988         
30989         var items = Roo.apply([], this.bricks);
30990         
30991         if(this.isHorizontal){
30992             this._horizontalLayoutItems( items , isInstant );
30993             return;
30994         }
30995         
30996 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30997 //            this._verticalAlternativeLayoutItems( items , isInstant );
30998 //            return;
30999 //        }
31000         
31001         this._verticalLayoutItems( items , isInstant );
31002         
31003     },
31004     
31005     _verticalLayoutItems : function ( items , isInstant)
31006     {
31007         if ( !items || !items.length ) {
31008             return;
31009         }
31010         
31011         var standard = [
31012             ['xs', 'xs', 'xs', 'tall'],
31013             ['xs', 'xs', 'tall'],
31014             ['xs', 'xs', 'sm'],
31015             ['xs', 'xs', 'xs'],
31016             ['xs', 'tall'],
31017             ['xs', 'sm'],
31018             ['xs', 'xs'],
31019             ['xs'],
31020             
31021             ['sm', 'xs', 'xs'],
31022             ['sm', 'xs'],
31023             ['sm'],
31024             
31025             ['tall', 'xs', 'xs', 'xs'],
31026             ['tall', 'xs', 'xs'],
31027             ['tall', 'xs'],
31028             ['tall']
31029             
31030         ];
31031         
31032         var queue = [];
31033         
31034         var boxes = [];
31035         
31036         var box = [];
31037         
31038         Roo.each(items, function(item, k){
31039             
31040             switch (item.size) {
31041                 // these layouts take up a full box,
31042                 case 'md' :
31043                 case 'md-left' :
31044                 case 'md-right' :
31045                 case 'wide' :
31046                     
31047                     if(box.length){
31048                         boxes.push(box);
31049                         box = [];
31050                     }
31051                     
31052                     boxes.push([item]);
31053                     
31054                     break;
31055                     
31056                 case 'xs' :
31057                 case 'sm' :
31058                 case 'tall' :
31059                     
31060                     box.push(item);
31061                     
31062                     break;
31063                 default :
31064                     break;
31065                     
31066             }
31067             
31068         }, this);
31069         
31070         if(box.length){
31071             boxes.push(box);
31072             box = [];
31073         }
31074         
31075         var filterPattern = function(box, length)
31076         {
31077             if(!box.length){
31078                 return;
31079             }
31080             
31081             var match = false;
31082             
31083             var pattern = box.slice(0, length);
31084             
31085             var format = [];
31086             
31087             Roo.each(pattern, function(i){
31088                 format.push(i.size);
31089             }, this);
31090             
31091             Roo.each(standard, function(s){
31092                 
31093                 if(String(s) != String(format)){
31094                     return;
31095                 }
31096                 
31097                 match = true;
31098                 return false;
31099                 
31100             }, this);
31101             
31102             if(!match && length == 1){
31103                 return;
31104             }
31105             
31106             if(!match){
31107                 filterPattern(box, length - 1);
31108                 return;
31109             }
31110                 
31111             queue.push(pattern);
31112
31113             box = box.slice(length, box.length);
31114
31115             filterPattern(box, 4);
31116
31117             return;
31118             
31119         }
31120         
31121         Roo.each(boxes, function(box, k){
31122             
31123             if(!box.length){
31124                 return;
31125             }
31126             
31127             if(box.length == 1){
31128                 queue.push(box);
31129                 return;
31130             }
31131             
31132             filterPattern(box, 4);
31133             
31134         }, this);
31135         
31136         this._processVerticalLayoutQueue( queue, isInstant );
31137         
31138     },
31139     
31140 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31141 //    {
31142 //        if ( !items || !items.length ) {
31143 //            return;
31144 //        }
31145 //
31146 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31147 //        
31148 //    },
31149     
31150     _horizontalLayoutItems : function ( items , isInstant)
31151     {
31152         if ( !items || !items.length || items.length < 3) {
31153             return;
31154         }
31155         
31156         items.reverse();
31157         
31158         var eItems = items.slice(0, 3);
31159         
31160         items = items.slice(3, items.length);
31161         
31162         var standard = [
31163             ['xs', 'xs', 'xs', 'wide'],
31164             ['xs', 'xs', 'wide'],
31165             ['xs', 'xs', 'sm'],
31166             ['xs', 'xs', 'xs'],
31167             ['xs', 'wide'],
31168             ['xs', 'sm'],
31169             ['xs', 'xs'],
31170             ['xs'],
31171             
31172             ['sm', 'xs', 'xs'],
31173             ['sm', 'xs'],
31174             ['sm'],
31175             
31176             ['wide', 'xs', 'xs', 'xs'],
31177             ['wide', 'xs', 'xs'],
31178             ['wide', 'xs'],
31179             ['wide'],
31180             
31181             ['wide-thin']
31182         ];
31183         
31184         var queue = [];
31185         
31186         var boxes = [];
31187         
31188         var box = [];
31189         
31190         Roo.each(items, function(item, k){
31191             
31192             switch (item.size) {
31193                 case 'md' :
31194                 case 'md-left' :
31195                 case 'md-right' :
31196                 case 'tall' :
31197                     
31198                     if(box.length){
31199                         boxes.push(box);
31200                         box = [];
31201                     }
31202                     
31203                     boxes.push([item]);
31204                     
31205                     break;
31206                     
31207                 case 'xs' :
31208                 case 'sm' :
31209                 case 'wide' :
31210                 case 'wide-thin' :
31211                     
31212                     box.push(item);
31213                     
31214                     break;
31215                 default :
31216                     break;
31217                     
31218             }
31219             
31220         }, this);
31221         
31222         if(box.length){
31223             boxes.push(box);
31224             box = [];
31225         }
31226         
31227         var filterPattern = function(box, length)
31228         {
31229             if(!box.length){
31230                 return;
31231             }
31232             
31233             var match = false;
31234             
31235             var pattern = box.slice(0, length);
31236             
31237             var format = [];
31238             
31239             Roo.each(pattern, function(i){
31240                 format.push(i.size);
31241             }, this);
31242             
31243             Roo.each(standard, function(s){
31244                 
31245                 if(String(s) != String(format)){
31246                     return;
31247                 }
31248                 
31249                 match = true;
31250                 return false;
31251                 
31252             }, this);
31253             
31254             if(!match && length == 1){
31255                 return;
31256             }
31257             
31258             if(!match){
31259                 filterPattern(box, length - 1);
31260                 return;
31261             }
31262                 
31263             queue.push(pattern);
31264
31265             box = box.slice(length, box.length);
31266
31267             filterPattern(box, 4);
31268
31269             return;
31270             
31271         }
31272         
31273         Roo.each(boxes, function(box, k){
31274             
31275             if(!box.length){
31276                 return;
31277             }
31278             
31279             if(box.length == 1){
31280                 queue.push(box);
31281                 return;
31282             }
31283             
31284             filterPattern(box, 4);
31285             
31286         }, this);
31287         
31288         
31289         var prune = [];
31290         
31291         var pos = this.el.getBox(true);
31292         
31293         var minX = pos.x;
31294         
31295         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31296         
31297         var hit_end = false;
31298         
31299         Roo.each(queue, function(box){
31300             
31301             if(hit_end){
31302                 
31303                 Roo.each(box, function(b){
31304                 
31305                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31306                     b.el.hide();
31307
31308                 }, this);
31309
31310                 return;
31311             }
31312             
31313             var mx = 0;
31314             
31315             Roo.each(box, function(b){
31316                 
31317                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31318                 b.el.show();
31319
31320                 mx = Math.max(mx, b.x);
31321                 
31322             }, this);
31323             
31324             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31325             
31326             if(maxX < minX){
31327                 
31328                 Roo.each(box, function(b){
31329                 
31330                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31331                     b.el.hide();
31332                     
31333                 }, this);
31334                 
31335                 hit_end = true;
31336                 
31337                 return;
31338             }
31339             
31340             prune.push(box);
31341             
31342         }, this);
31343         
31344         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31345     },
31346     
31347     /** Sets position of item in DOM
31348     * @param {Element} item
31349     * @param {Number} x - horizontal position
31350     * @param {Number} y - vertical position
31351     * @param {Boolean} isInstant - disables transitions
31352     */
31353     _processVerticalLayoutQueue : function( queue, isInstant )
31354     {
31355         var pos = this.el.getBox(true);
31356         var x = pos.x;
31357         var y = pos.y;
31358         var maxY = [];
31359         
31360         for (var i = 0; i < this.cols; i++){
31361             maxY[i] = pos.y;
31362         }
31363         
31364         Roo.each(queue, function(box, k){
31365             
31366             var col = k % this.cols;
31367             
31368             Roo.each(box, function(b,kk){
31369                 
31370                 b.el.position('absolute');
31371                 
31372                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31373                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31374                 
31375                 if(b.size == 'md-left' || b.size == 'md-right'){
31376                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31377                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31378                 }
31379                 
31380                 b.el.setWidth(width);
31381                 b.el.setHeight(height);
31382                 // iframe?
31383                 b.el.select('iframe',true).setSize(width,height);
31384                 
31385             }, this);
31386             
31387             for (var i = 0; i < this.cols; i++){
31388                 
31389                 if(maxY[i] < maxY[col]){
31390                     col = i;
31391                     continue;
31392                 }
31393                 
31394                 col = Math.min(col, i);
31395                 
31396             }
31397             
31398             x = pos.x + col * (this.colWidth + this.padWidth);
31399             
31400             y = maxY[col];
31401             
31402             var positions = [];
31403             
31404             switch (box.length){
31405                 case 1 :
31406                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31407                     break;
31408                 case 2 :
31409                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31410                     break;
31411                 case 3 :
31412                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31413                     break;
31414                 case 4 :
31415                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31416                     break;
31417                 default :
31418                     break;
31419             }
31420             
31421             Roo.each(box, function(b,kk){
31422                 
31423                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31424                 
31425                 var sz = b.el.getSize();
31426                 
31427                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31428                 
31429             }, this);
31430             
31431         }, this);
31432         
31433         var mY = 0;
31434         
31435         for (var i = 0; i < this.cols; i++){
31436             mY = Math.max(mY, maxY[i]);
31437         }
31438         
31439         this.el.setHeight(mY - pos.y);
31440         
31441     },
31442     
31443 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31444 //    {
31445 //        var pos = this.el.getBox(true);
31446 //        var x = pos.x;
31447 //        var y = pos.y;
31448 //        var maxX = pos.right;
31449 //        
31450 //        var maxHeight = 0;
31451 //        
31452 //        Roo.each(items, function(item, k){
31453 //            
31454 //            var c = k % 2;
31455 //            
31456 //            item.el.position('absolute');
31457 //                
31458 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31459 //
31460 //            item.el.setWidth(width);
31461 //
31462 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31463 //
31464 //            item.el.setHeight(height);
31465 //            
31466 //            if(c == 0){
31467 //                item.el.setXY([x, y], isInstant ? false : true);
31468 //            } else {
31469 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31470 //            }
31471 //            
31472 //            y = y + height + this.alternativePadWidth;
31473 //            
31474 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31475 //            
31476 //        }, this);
31477 //        
31478 //        this.el.setHeight(maxHeight);
31479 //        
31480 //    },
31481     
31482     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31483     {
31484         var pos = this.el.getBox(true);
31485         
31486         var minX = pos.x;
31487         var minY = pos.y;
31488         
31489         var maxX = pos.right;
31490         
31491         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31492         
31493         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31494         
31495         Roo.each(queue, function(box, k){
31496             
31497             Roo.each(box, function(b, kk){
31498                 
31499                 b.el.position('absolute');
31500                 
31501                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31502                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31503                 
31504                 if(b.size == 'md-left' || b.size == 'md-right'){
31505                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31506                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31507                 }
31508                 
31509                 b.el.setWidth(width);
31510                 b.el.setHeight(height);
31511                 
31512             }, this);
31513             
31514             if(!box.length){
31515                 return;
31516             }
31517             
31518             var positions = [];
31519             
31520             switch (box.length){
31521                 case 1 :
31522                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31523                     break;
31524                 case 2 :
31525                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31526                     break;
31527                 case 3 :
31528                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31529                     break;
31530                 case 4 :
31531                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31532                     break;
31533                 default :
31534                     break;
31535             }
31536             
31537             Roo.each(box, function(b,kk){
31538                 
31539                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31540                 
31541                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31542                 
31543             }, this);
31544             
31545         }, this);
31546         
31547     },
31548     
31549     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31550     {
31551         Roo.each(eItems, function(b,k){
31552             
31553             b.size = (k == 0) ? 'sm' : 'xs';
31554             b.x = (k == 0) ? 2 : 1;
31555             b.y = (k == 0) ? 2 : 1;
31556             
31557             b.el.position('absolute');
31558             
31559             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31560                 
31561             b.el.setWidth(width);
31562             
31563             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31564             
31565             b.el.setHeight(height);
31566             
31567         }, this);
31568
31569         var positions = [];
31570         
31571         positions.push({
31572             x : maxX - this.unitWidth * 2 - this.gutter,
31573             y : minY
31574         });
31575         
31576         positions.push({
31577             x : maxX - this.unitWidth,
31578             y : minY + (this.unitWidth + this.gutter) * 2
31579         });
31580         
31581         positions.push({
31582             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31583             y : minY
31584         });
31585         
31586         Roo.each(eItems, function(b,k){
31587             
31588             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31589
31590         }, this);
31591         
31592     },
31593     
31594     getVerticalOneBoxColPositions : function(x, y, box)
31595     {
31596         var pos = [];
31597         
31598         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31599         
31600         if(box[0].size == 'md-left'){
31601             rand = 0;
31602         }
31603         
31604         if(box[0].size == 'md-right'){
31605             rand = 1;
31606         }
31607         
31608         pos.push({
31609             x : x + (this.unitWidth + this.gutter) * rand,
31610             y : y
31611         });
31612         
31613         return pos;
31614     },
31615     
31616     getVerticalTwoBoxColPositions : function(x, y, box)
31617     {
31618         var pos = [];
31619         
31620         if(box[0].size == 'xs'){
31621             
31622             pos.push({
31623                 x : x,
31624                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31625             });
31626
31627             pos.push({
31628                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31629                 y : y
31630             });
31631             
31632             return pos;
31633             
31634         }
31635         
31636         pos.push({
31637             x : x,
31638             y : y
31639         });
31640
31641         pos.push({
31642             x : x + (this.unitWidth + this.gutter) * 2,
31643             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31644         });
31645         
31646         return pos;
31647         
31648     },
31649     
31650     getVerticalThreeBoxColPositions : function(x, y, box)
31651     {
31652         var pos = [];
31653         
31654         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31655             
31656             pos.push({
31657                 x : x,
31658                 y : y
31659             });
31660
31661             pos.push({
31662                 x : x + (this.unitWidth + this.gutter) * 1,
31663                 y : y
31664             });
31665             
31666             pos.push({
31667                 x : x + (this.unitWidth + this.gutter) * 2,
31668                 y : y
31669             });
31670             
31671             return pos;
31672             
31673         }
31674         
31675         if(box[0].size == 'xs' && box[1].size == 'xs'){
31676             
31677             pos.push({
31678                 x : x,
31679                 y : y
31680             });
31681
31682             pos.push({
31683                 x : x,
31684                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31685             });
31686             
31687             pos.push({
31688                 x : x + (this.unitWidth + this.gutter) * 1,
31689                 y : y
31690             });
31691             
31692             return pos;
31693             
31694         }
31695         
31696         pos.push({
31697             x : x,
31698             y : y
31699         });
31700
31701         pos.push({
31702             x : x + (this.unitWidth + this.gutter) * 2,
31703             y : y
31704         });
31705
31706         pos.push({
31707             x : x + (this.unitWidth + this.gutter) * 2,
31708             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31709         });
31710             
31711         return pos;
31712         
31713     },
31714     
31715     getVerticalFourBoxColPositions : function(x, y, box)
31716     {
31717         var pos = [];
31718         
31719         if(box[0].size == 'xs'){
31720             
31721             pos.push({
31722                 x : x,
31723                 y : y
31724             });
31725
31726             pos.push({
31727                 x : x,
31728                 y : y + (this.unitHeight + this.gutter) * 1
31729             });
31730             
31731             pos.push({
31732                 x : x,
31733                 y : y + (this.unitHeight + this.gutter) * 2
31734             });
31735             
31736             pos.push({
31737                 x : x + (this.unitWidth + this.gutter) * 1,
31738                 y : y
31739             });
31740             
31741             return pos;
31742             
31743         }
31744         
31745         pos.push({
31746             x : x,
31747             y : y
31748         });
31749
31750         pos.push({
31751             x : x + (this.unitWidth + this.gutter) * 2,
31752             y : y
31753         });
31754
31755         pos.push({
31756             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31757             y : y + (this.unitHeight + this.gutter) * 1
31758         });
31759
31760         pos.push({
31761             x : x + (this.unitWidth + this.gutter) * 2,
31762             y : y + (this.unitWidth + this.gutter) * 2
31763         });
31764
31765         return pos;
31766         
31767     },
31768     
31769     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31770     {
31771         var pos = [];
31772         
31773         if(box[0].size == 'md-left'){
31774             pos.push({
31775                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31776                 y : minY
31777             });
31778             
31779             return pos;
31780         }
31781         
31782         if(box[0].size == 'md-right'){
31783             pos.push({
31784                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31785                 y : minY + (this.unitWidth + this.gutter) * 1
31786             });
31787             
31788             return pos;
31789         }
31790         
31791         var rand = Math.floor(Math.random() * (4 - box[0].y));
31792         
31793         pos.push({
31794             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31795             y : minY + (this.unitWidth + this.gutter) * rand
31796         });
31797         
31798         return pos;
31799         
31800     },
31801     
31802     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31803     {
31804         var pos = [];
31805         
31806         if(box[0].size == 'xs'){
31807             
31808             pos.push({
31809                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31810                 y : minY
31811             });
31812
31813             pos.push({
31814                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31815                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31816             });
31817             
31818             return pos;
31819             
31820         }
31821         
31822         pos.push({
31823             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31824             y : minY
31825         });
31826
31827         pos.push({
31828             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31829             y : minY + (this.unitWidth + this.gutter) * 2
31830         });
31831         
31832         return pos;
31833         
31834     },
31835     
31836     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31837     {
31838         var pos = [];
31839         
31840         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31841             
31842             pos.push({
31843                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31844                 y : minY
31845             });
31846
31847             pos.push({
31848                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31849                 y : minY + (this.unitWidth + this.gutter) * 1
31850             });
31851             
31852             pos.push({
31853                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31854                 y : minY + (this.unitWidth + this.gutter) * 2
31855             });
31856             
31857             return pos;
31858             
31859         }
31860         
31861         if(box[0].size == 'xs' && box[1].size == 'xs'){
31862             
31863             pos.push({
31864                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31865                 y : minY
31866             });
31867
31868             pos.push({
31869                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31870                 y : minY
31871             });
31872             
31873             pos.push({
31874                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31875                 y : minY + (this.unitWidth + this.gutter) * 1
31876             });
31877             
31878             return pos;
31879             
31880         }
31881         
31882         pos.push({
31883             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31884             y : minY
31885         });
31886
31887         pos.push({
31888             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31889             y : minY + (this.unitWidth + this.gutter) * 2
31890         });
31891
31892         pos.push({
31893             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31894             y : minY + (this.unitWidth + this.gutter) * 2
31895         });
31896             
31897         return pos;
31898         
31899     },
31900     
31901     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31902     {
31903         var pos = [];
31904         
31905         if(box[0].size == 'xs'){
31906             
31907             pos.push({
31908                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31909                 y : minY
31910             });
31911
31912             pos.push({
31913                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31914                 y : minY
31915             });
31916             
31917             pos.push({
31918                 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),
31919                 y : minY
31920             });
31921             
31922             pos.push({
31923                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31924                 y : minY + (this.unitWidth + this.gutter) * 1
31925             });
31926             
31927             return pos;
31928             
31929         }
31930         
31931         pos.push({
31932             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31933             y : minY
31934         });
31935         
31936         pos.push({
31937             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31938             y : minY + (this.unitWidth + this.gutter) * 2
31939         });
31940         
31941         pos.push({
31942             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31943             y : minY + (this.unitWidth + this.gutter) * 2
31944         });
31945         
31946         pos.push({
31947             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),
31948             y : minY + (this.unitWidth + this.gutter) * 2
31949         });
31950
31951         return pos;
31952         
31953     },
31954     
31955     /**
31956     * remove a Masonry Brick
31957     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31958     */
31959     removeBrick : function(brick_id)
31960     {
31961         if (!brick_id) {
31962             return;
31963         }
31964         
31965         for (var i = 0; i<this.bricks.length; i++) {
31966             if (this.bricks[i].id == brick_id) {
31967                 this.bricks.splice(i,1);
31968                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31969                 this.initial();
31970             }
31971         }
31972     },
31973     
31974     /**
31975     * adds a Masonry Brick
31976     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31977     */
31978     addBrick : function(cfg)
31979     {
31980         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31981         //this.register(cn);
31982         cn.parentId = this.id;
31983         cn.onRender(this.el, null);
31984         return cn;
31985     },
31986     
31987     /**
31988     * register a Masonry Brick
31989     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31990     */
31991     
31992     register : function(brick)
31993     {
31994         this.bricks.push(brick);
31995         brick.masonryId = this.id;
31996     },
31997     
31998     /**
31999     * clear all the Masonry Brick
32000     */
32001     clearAll : function()
32002     {
32003         this.bricks = [];
32004         //this.getChildContainer().dom.innerHTML = "";
32005         this.el.dom.innerHTML = '';
32006     },
32007     
32008     getSelected : function()
32009     {
32010         if (!this.selectedBrick) {
32011             return false;
32012         }
32013         
32014         return this.selectedBrick;
32015     }
32016 });
32017
32018 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32019     
32020     groups: {},
32021      /**
32022     * register a Masonry Layout
32023     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32024     */
32025     
32026     register : function(layout)
32027     {
32028         this.groups[layout.id] = layout;
32029     },
32030     /**
32031     * fetch a  Masonry Layout based on the masonry layout ID
32032     * @param {string} the masonry layout to add
32033     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32034     */
32035     
32036     get: function(layout_id) {
32037         if (typeof(this.groups[layout_id]) == 'undefined') {
32038             return false;
32039         }
32040         return this.groups[layout_id] ;
32041     }
32042     
32043     
32044     
32045 });
32046
32047  
32048
32049  /**
32050  *
32051  * This is based on 
32052  * http://masonry.desandro.com
32053  *
32054  * The idea is to render all the bricks based on vertical width...
32055  *
32056  * The original code extends 'outlayer' - we might need to use that....
32057  * 
32058  */
32059
32060
32061 /**
32062  * @class Roo.bootstrap.LayoutMasonryAuto
32063  * @extends Roo.bootstrap.Component
32064  * Bootstrap Layout Masonry class
32065  * 
32066  * @constructor
32067  * Create a new Element
32068  * @param {Object} config The config object
32069  */
32070
32071 Roo.bootstrap.LayoutMasonryAuto = function(config){
32072     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32073 };
32074
32075 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32076     
32077       /**
32078      * @cfg {Boolean} isFitWidth  - resize the width..
32079      */   
32080     isFitWidth : false,  // options..
32081     /**
32082      * @cfg {Boolean} isOriginLeft = left align?
32083      */   
32084     isOriginLeft : true,
32085     /**
32086      * @cfg {Boolean} isOriginTop = top align?
32087      */   
32088     isOriginTop : false,
32089     /**
32090      * @cfg {Boolean} isLayoutInstant = no animation?
32091      */   
32092     isLayoutInstant : false, // needed?
32093     /**
32094      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32095      */   
32096     isResizingContainer : true,
32097     /**
32098      * @cfg {Number} columnWidth  width of the columns 
32099      */   
32100     
32101     columnWidth : 0,
32102     
32103     /**
32104      * @cfg {Number} maxCols maximum number of columns
32105      */   
32106     
32107     maxCols: 0,
32108     /**
32109      * @cfg {Number} padHeight padding below box..
32110      */   
32111     
32112     padHeight : 10, 
32113     
32114     /**
32115      * @cfg {Boolean} isAutoInitial defalut true
32116      */   
32117     
32118     isAutoInitial : true, 
32119     
32120     // private?
32121     gutter : 0,
32122     
32123     containerWidth: 0,
32124     initialColumnWidth : 0,
32125     currentSize : null,
32126     
32127     colYs : null, // array.
32128     maxY : 0,
32129     padWidth: 10,
32130     
32131     
32132     tag: 'div',
32133     cls: '',
32134     bricks: null, //CompositeElement
32135     cols : 0, // array?
32136     // element : null, // wrapped now this.el
32137     _isLayoutInited : null, 
32138     
32139     
32140     getAutoCreate : function(){
32141         
32142         var cfg = {
32143             tag: this.tag,
32144             cls: 'blog-masonary-wrapper ' + this.cls,
32145             cn : {
32146                 cls : 'mas-boxes masonary'
32147             }
32148         };
32149         
32150         return cfg;
32151     },
32152     
32153     getChildContainer: function( )
32154     {
32155         if (this.boxesEl) {
32156             return this.boxesEl;
32157         }
32158         
32159         this.boxesEl = this.el.select('.mas-boxes').first();
32160         
32161         return this.boxesEl;
32162     },
32163     
32164     
32165     initEvents : function()
32166     {
32167         var _this = this;
32168         
32169         if(this.isAutoInitial){
32170             Roo.log('hook children rendered');
32171             this.on('childrenrendered', function() {
32172                 Roo.log('children rendered');
32173                 _this.initial();
32174             } ,this);
32175         }
32176         
32177     },
32178     
32179     initial : function()
32180     {
32181         this.reloadItems();
32182
32183         this.currentSize = this.el.getBox(true);
32184
32185         /// was window resize... - let's see if this works..
32186         Roo.EventManager.onWindowResize(this.resize, this); 
32187
32188         if(!this.isAutoInitial){
32189             this.layout();
32190             return;
32191         }
32192         
32193         this.layout.defer(500,this);
32194     },
32195     
32196     reloadItems: function()
32197     {
32198         this.bricks = this.el.select('.masonry-brick', true);
32199         
32200         this.bricks.each(function(b) {
32201             //Roo.log(b.getSize());
32202             if (!b.attr('originalwidth')) {
32203                 b.attr('originalwidth',  b.getSize().width);
32204             }
32205             
32206         });
32207         
32208         Roo.log(this.bricks.elements.length);
32209     },
32210     
32211     resize : function()
32212     {
32213         Roo.log('resize');
32214         var cs = this.el.getBox(true);
32215         
32216         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32217             Roo.log("no change in with or X");
32218             return;
32219         }
32220         this.currentSize = cs;
32221         this.layout();
32222     },
32223     
32224     layout : function()
32225     {
32226          Roo.log('layout');
32227         this._resetLayout();
32228         //this._manageStamps();
32229       
32230         // don't animate first layout
32231         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32232         this.layoutItems( isInstant );
32233       
32234         // flag for initalized
32235         this._isLayoutInited = true;
32236     },
32237     
32238     layoutItems : function( isInstant )
32239     {
32240         //var items = this._getItemsForLayout( this.items );
32241         // original code supports filtering layout items.. we just ignore it..
32242         
32243         this._layoutItems( this.bricks , isInstant );
32244       
32245         this._postLayout();
32246     },
32247     _layoutItems : function ( items , isInstant)
32248     {
32249        //this.fireEvent( 'layout', this, items );
32250     
32251
32252         if ( !items || !items.elements.length ) {
32253           // no items, emit event with empty array
32254             return;
32255         }
32256
32257         var queue = [];
32258         items.each(function(item) {
32259             Roo.log("layout item");
32260             Roo.log(item);
32261             // get x/y object from method
32262             var position = this._getItemLayoutPosition( item );
32263             // enqueue
32264             position.item = item;
32265             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32266             queue.push( position );
32267         }, this);
32268       
32269         this._processLayoutQueue( queue );
32270     },
32271     /** Sets position of item in DOM
32272     * @param {Element} item
32273     * @param {Number} x - horizontal position
32274     * @param {Number} y - vertical position
32275     * @param {Boolean} isInstant - disables transitions
32276     */
32277     _processLayoutQueue : function( queue )
32278     {
32279         for ( var i=0, len = queue.length; i < len; i++ ) {
32280             var obj = queue[i];
32281             obj.item.position('absolute');
32282             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32283         }
32284     },
32285       
32286     
32287     /**
32288     * Any logic you want to do after each layout,
32289     * i.e. size the container
32290     */
32291     _postLayout : function()
32292     {
32293         this.resizeContainer();
32294     },
32295     
32296     resizeContainer : function()
32297     {
32298         if ( !this.isResizingContainer ) {
32299             return;
32300         }
32301         var size = this._getContainerSize();
32302         if ( size ) {
32303             this.el.setSize(size.width,size.height);
32304             this.boxesEl.setSize(size.width,size.height);
32305         }
32306     },
32307     
32308     
32309     
32310     _resetLayout : function()
32311     {
32312         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32313         this.colWidth = this.el.getWidth();
32314         //this.gutter = this.el.getWidth(); 
32315         
32316         this.measureColumns();
32317
32318         // reset column Y
32319         var i = this.cols;
32320         this.colYs = [];
32321         while (i--) {
32322             this.colYs.push( 0 );
32323         }
32324     
32325         this.maxY = 0;
32326     },
32327
32328     measureColumns : function()
32329     {
32330         this.getContainerWidth();
32331       // if columnWidth is 0, default to outerWidth of first item
32332         if ( !this.columnWidth ) {
32333             var firstItem = this.bricks.first();
32334             Roo.log(firstItem);
32335             this.columnWidth  = this.containerWidth;
32336             if (firstItem && firstItem.attr('originalwidth') ) {
32337                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32338             }
32339             // columnWidth fall back to item of first element
32340             Roo.log("set column width?");
32341                         this.initialColumnWidth = this.columnWidth  ;
32342
32343             // if first elem has no width, default to size of container
32344             
32345         }
32346         
32347         
32348         if (this.initialColumnWidth) {
32349             this.columnWidth = this.initialColumnWidth;
32350         }
32351         
32352         
32353             
32354         // column width is fixed at the top - however if container width get's smaller we should
32355         // reduce it...
32356         
32357         // this bit calcs how man columns..
32358             
32359         var columnWidth = this.columnWidth += this.gutter;
32360       
32361         // calculate columns
32362         var containerWidth = this.containerWidth + this.gutter;
32363         
32364         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32365         // fix rounding errors, typically with gutters
32366         var excess = columnWidth - containerWidth % columnWidth;
32367         
32368         
32369         // if overshoot is less than a pixel, round up, otherwise floor it
32370         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32371         cols = Math[ mathMethod ]( cols );
32372         this.cols = Math.max( cols, 1 );
32373         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32374         
32375          // padding positioning..
32376         var totalColWidth = this.cols * this.columnWidth;
32377         var padavail = this.containerWidth - totalColWidth;
32378         // so for 2 columns - we need 3 'pads'
32379         
32380         var padNeeded = (1+this.cols) * this.padWidth;
32381         
32382         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32383         
32384         this.columnWidth += padExtra
32385         //this.padWidth = Math.floor(padavail /  ( this.cols));
32386         
32387         // adjust colum width so that padding is fixed??
32388         
32389         // we have 3 columns ... total = width * 3
32390         // we have X left over... that should be used by 
32391         
32392         //if (this.expandC) {
32393             
32394         //}
32395         
32396         
32397         
32398     },
32399     
32400     getContainerWidth : function()
32401     {
32402        /* // container is parent if fit width
32403         var container = this.isFitWidth ? this.element.parentNode : this.element;
32404         // check that this.size and size are there
32405         // IE8 triggers resize on body size change, so they might not be
32406         
32407         var size = getSize( container );  //FIXME
32408         this.containerWidth = size && size.innerWidth; //FIXME
32409         */
32410          
32411         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32412         
32413     },
32414     
32415     _getItemLayoutPosition : function( item )  // what is item?
32416     {
32417         // we resize the item to our columnWidth..
32418       
32419         item.setWidth(this.columnWidth);
32420         item.autoBoxAdjust  = false;
32421         
32422         var sz = item.getSize();
32423  
32424         // how many columns does this brick span
32425         var remainder = this.containerWidth % this.columnWidth;
32426         
32427         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32428         // round if off by 1 pixel, otherwise use ceil
32429         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32430         colSpan = Math.min( colSpan, this.cols );
32431         
32432         // normally this should be '1' as we dont' currently allow multi width columns..
32433         
32434         var colGroup = this._getColGroup( colSpan );
32435         // get the minimum Y value from the columns
32436         var minimumY = Math.min.apply( Math, colGroup );
32437         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32438         
32439         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32440          
32441         // position the brick
32442         var position = {
32443             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32444             y: this.currentSize.y + minimumY + this.padHeight
32445         };
32446         
32447         Roo.log(position);
32448         // apply setHeight to necessary columns
32449         var setHeight = minimumY + sz.height + this.padHeight;
32450         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32451         
32452         var setSpan = this.cols + 1 - colGroup.length;
32453         for ( var i = 0; i < setSpan; i++ ) {
32454           this.colYs[ shortColIndex + i ] = setHeight ;
32455         }
32456       
32457         return position;
32458     },
32459     
32460     /**
32461      * @param {Number} colSpan - number of columns the element spans
32462      * @returns {Array} colGroup
32463      */
32464     _getColGroup : function( colSpan )
32465     {
32466         if ( colSpan < 2 ) {
32467           // if brick spans only one column, use all the column Ys
32468           return this.colYs;
32469         }
32470       
32471         var colGroup = [];
32472         // how many different places could this brick fit horizontally
32473         var groupCount = this.cols + 1 - colSpan;
32474         // for each group potential horizontal position
32475         for ( var i = 0; i < groupCount; i++ ) {
32476           // make an array of colY values for that one group
32477           var groupColYs = this.colYs.slice( i, i + colSpan );
32478           // and get the max value of the array
32479           colGroup[i] = Math.max.apply( Math, groupColYs );
32480         }
32481         return colGroup;
32482     },
32483     /*
32484     _manageStamp : function( stamp )
32485     {
32486         var stampSize =  stamp.getSize();
32487         var offset = stamp.getBox();
32488         // get the columns that this stamp affects
32489         var firstX = this.isOriginLeft ? offset.x : offset.right;
32490         var lastX = firstX + stampSize.width;
32491         var firstCol = Math.floor( firstX / this.columnWidth );
32492         firstCol = Math.max( 0, firstCol );
32493         
32494         var lastCol = Math.floor( lastX / this.columnWidth );
32495         // lastCol should not go over if multiple of columnWidth #425
32496         lastCol -= lastX % this.columnWidth ? 0 : 1;
32497         lastCol = Math.min( this.cols - 1, lastCol );
32498         
32499         // set colYs to bottom of the stamp
32500         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32501             stampSize.height;
32502             
32503         for ( var i = firstCol; i <= lastCol; i++ ) {
32504           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32505         }
32506     },
32507     */
32508     
32509     _getContainerSize : function()
32510     {
32511         this.maxY = Math.max.apply( Math, this.colYs );
32512         var size = {
32513             height: this.maxY
32514         };
32515       
32516         if ( this.isFitWidth ) {
32517             size.width = this._getContainerFitWidth();
32518         }
32519       
32520         return size;
32521     },
32522     
32523     _getContainerFitWidth : function()
32524     {
32525         var unusedCols = 0;
32526         // count unused columns
32527         var i = this.cols;
32528         while ( --i ) {
32529           if ( this.colYs[i] !== 0 ) {
32530             break;
32531           }
32532           unusedCols++;
32533         }
32534         // fit container to columns that have been used
32535         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32536     },
32537     
32538     needsResizeLayout : function()
32539     {
32540         var previousWidth = this.containerWidth;
32541         this.getContainerWidth();
32542         return previousWidth !== this.containerWidth;
32543     }
32544  
32545 });
32546
32547  
32548
32549  /*
32550  * - LGPL
32551  *
32552  * element
32553  * 
32554  */
32555
32556 /**
32557  * @class Roo.bootstrap.MasonryBrick
32558  * @extends Roo.bootstrap.Component
32559  * Bootstrap MasonryBrick class
32560  * 
32561  * @constructor
32562  * Create a new MasonryBrick
32563  * @param {Object} config The config object
32564  */
32565
32566 Roo.bootstrap.MasonryBrick = function(config){
32567     
32568     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32569     
32570     Roo.bootstrap.MasonryBrick.register(this);
32571     
32572     this.addEvents({
32573         // raw events
32574         /**
32575          * @event click
32576          * When a MasonryBrick is clcik
32577          * @param {Roo.bootstrap.MasonryBrick} this
32578          * @param {Roo.EventObject} e
32579          */
32580         "click" : true
32581     });
32582 };
32583
32584 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32585     
32586     /**
32587      * @cfg {String} title
32588      */   
32589     title : '',
32590     /**
32591      * @cfg {String} html
32592      */   
32593     html : '',
32594     /**
32595      * @cfg {String} bgimage
32596      */   
32597     bgimage : '',
32598     /**
32599      * @cfg {String} videourl
32600      */   
32601     videourl : '',
32602     /**
32603      * @cfg {String} cls
32604      */   
32605     cls : '',
32606     /**
32607      * @cfg {String} href
32608      */   
32609     href : '',
32610     /**
32611      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32612      */   
32613     size : 'xs',
32614     
32615     /**
32616      * @cfg {String} placetitle (center|bottom)
32617      */   
32618     placetitle : '',
32619     
32620     /**
32621      * @cfg {Boolean} isFitContainer defalut true
32622      */   
32623     isFitContainer : true, 
32624     
32625     /**
32626      * @cfg {Boolean} preventDefault defalut false
32627      */   
32628     preventDefault : false, 
32629     
32630     /**
32631      * @cfg {Boolean} inverse defalut false
32632      */   
32633     maskInverse : false, 
32634     
32635     getAutoCreate : function()
32636     {
32637         if(!this.isFitContainer){
32638             return this.getSplitAutoCreate();
32639         }
32640         
32641         var cls = 'masonry-brick masonry-brick-full';
32642         
32643         if(this.href.length){
32644             cls += ' masonry-brick-link';
32645         }
32646         
32647         if(this.bgimage.length){
32648             cls += ' masonry-brick-image';
32649         }
32650         
32651         if(this.maskInverse){
32652             cls += ' mask-inverse';
32653         }
32654         
32655         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32656             cls += ' enable-mask';
32657         }
32658         
32659         if(this.size){
32660             cls += ' masonry-' + this.size + '-brick';
32661         }
32662         
32663         if(this.placetitle.length){
32664             
32665             switch (this.placetitle) {
32666                 case 'center' :
32667                     cls += ' masonry-center-title';
32668                     break;
32669                 case 'bottom' :
32670                     cls += ' masonry-bottom-title';
32671                     break;
32672                 default:
32673                     break;
32674             }
32675             
32676         } else {
32677             if(!this.html.length && !this.bgimage.length){
32678                 cls += ' masonry-center-title';
32679             }
32680
32681             if(!this.html.length && this.bgimage.length){
32682                 cls += ' masonry-bottom-title';
32683             }
32684         }
32685         
32686         if(this.cls){
32687             cls += ' ' + this.cls;
32688         }
32689         
32690         var cfg = {
32691             tag: (this.href.length) ? 'a' : 'div',
32692             cls: cls,
32693             cn: [
32694                 {
32695                     tag: 'div',
32696                     cls: 'masonry-brick-mask'
32697                 },
32698                 {
32699                     tag: 'div',
32700                     cls: 'masonry-brick-paragraph',
32701                     cn: []
32702                 }
32703             ]
32704         };
32705         
32706         if(this.href.length){
32707             cfg.href = this.href;
32708         }
32709         
32710         var cn = cfg.cn[1].cn;
32711         
32712         if(this.title.length){
32713             cn.push({
32714                 tag: 'h4',
32715                 cls: 'masonry-brick-title',
32716                 html: this.title
32717             });
32718         }
32719         
32720         if(this.html.length){
32721             cn.push({
32722                 tag: 'p',
32723                 cls: 'masonry-brick-text',
32724                 html: this.html
32725             });
32726         }
32727         
32728         if (!this.title.length && !this.html.length) {
32729             cfg.cn[1].cls += ' hide';
32730         }
32731         
32732         if(this.bgimage.length){
32733             cfg.cn.push({
32734                 tag: 'img',
32735                 cls: 'masonry-brick-image-view',
32736                 src: this.bgimage
32737             });
32738         }
32739         
32740         if(this.videourl.length){
32741             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32742             // youtube support only?
32743             cfg.cn.push({
32744                 tag: 'iframe',
32745                 cls: 'masonry-brick-image-view',
32746                 src: vurl,
32747                 frameborder : 0,
32748                 allowfullscreen : true
32749             });
32750         }
32751         
32752         return cfg;
32753         
32754     },
32755     
32756     getSplitAutoCreate : function()
32757     {
32758         var cls = 'masonry-brick masonry-brick-split';
32759         
32760         if(this.href.length){
32761             cls += ' masonry-brick-link';
32762         }
32763         
32764         if(this.bgimage.length){
32765             cls += ' masonry-brick-image';
32766         }
32767         
32768         if(this.size){
32769             cls += ' masonry-' + this.size + '-brick';
32770         }
32771         
32772         switch (this.placetitle) {
32773             case 'center' :
32774                 cls += ' masonry-center-title';
32775                 break;
32776             case 'bottom' :
32777                 cls += ' masonry-bottom-title';
32778                 break;
32779             default:
32780                 if(!this.bgimage.length){
32781                     cls += ' masonry-center-title';
32782                 }
32783
32784                 if(this.bgimage.length){
32785                     cls += ' masonry-bottom-title';
32786                 }
32787                 break;
32788         }
32789         
32790         if(this.cls){
32791             cls += ' ' + this.cls;
32792         }
32793         
32794         var cfg = {
32795             tag: (this.href.length) ? 'a' : 'div',
32796             cls: cls,
32797             cn: [
32798                 {
32799                     tag: 'div',
32800                     cls: 'masonry-brick-split-head',
32801                     cn: [
32802                         {
32803                             tag: 'div',
32804                             cls: 'masonry-brick-paragraph',
32805                             cn: []
32806                         }
32807                     ]
32808                 },
32809                 {
32810                     tag: 'div',
32811                     cls: 'masonry-brick-split-body',
32812                     cn: []
32813                 }
32814             ]
32815         };
32816         
32817         if(this.href.length){
32818             cfg.href = this.href;
32819         }
32820         
32821         if(this.title.length){
32822             cfg.cn[0].cn[0].cn.push({
32823                 tag: 'h4',
32824                 cls: 'masonry-brick-title',
32825                 html: this.title
32826             });
32827         }
32828         
32829         if(this.html.length){
32830             cfg.cn[1].cn.push({
32831                 tag: 'p',
32832                 cls: 'masonry-brick-text',
32833                 html: this.html
32834             });
32835         }
32836
32837         if(this.bgimage.length){
32838             cfg.cn[0].cn.push({
32839                 tag: 'img',
32840                 cls: 'masonry-brick-image-view',
32841                 src: this.bgimage
32842             });
32843         }
32844         
32845         if(this.videourl.length){
32846             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32847             // youtube support only?
32848             cfg.cn[0].cn.cn.push({
32849                 tag: 'iframe',
32850                 cls: 'masonry-brick-image-view',
32851                 src: vurl,
32852                 frameborder : 0,
32853                 allowfullscreen : true
32854             });
32855         }
32856         
32857         return cfg;
32858     },
32859     
32860     initEvents: function() 
32861     {
32862         switch (this.size) {
32863             case 'xs' :
32864                 this.x = 1;
32865                 this.y = 1;
32866                 break;
32867             case 'sm' :
32868                 this.x = 2;
32869                 this.y = 2;
32870                 break;
32871             case 'md' :
32872             case 'md-left' :
32873             case 'md-right' :
32874                 this.x = 3;
32875                 this.y = 3;
32876                 break;
32877             case 'tall' :
32878                 this.x = 2;
32879                 this.y = 3;
32880                 break;
32881             case 'wide' :
32882                 this.x = 3;
32883                 this.y = 2;
32884                 break;
32885             case 'wide-thin' :
32886                 this.x = 3;
32887                 this.y = 1;
32888                 break;
32889                         
32890             default :
32891                 break;
32892         }
32893         
32894         if(Roo.isTouch){
32895             this.el.on('touchstart', this.onTouchStart, this);
32896             this.el.on('touchmove', this.onTouchMove, this);
32897             this.el.on('touchend', this.onTouchEnd, this);
32898             this.el.on('contextmenu', this.onContextMenu, this);
32899         } else {
32900             this.el.on('mouseenter'  ,this.enter, this);
32901             this.el.on('mouseleave', this.leave, this);
32902             this.el.on('click', this.onClick, this);
32903         }
32904         
32905         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32906             this.parent().bricks.push(this);   
32907         }
32908         
32909     },
32910     
32911     onClick: function(e, el)
32912     {
32913         var time = this.endTimer - this.startTimer;
32914         // Roo.log(e.preventDefault());
32915         if(Roo.isTouch){
32916             if(time > 1000){
32917                 e.preventDefault();
32918                 return;
32919             }
32920         }
32921         
32922         if(!this.preventDefault){
32923             return;
32924         }
32925         
32926         e.preventDefault();
32927         
32928         if (this.activeClass != '') {
32929             this.selectBrick();
32930         }
32931         
32932         this.fireEvent('click', this, e);
32933     },
32934     
32935     enter: function(e, el)
32936     {
32937         e.preventDefault();
32938         
32939         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32940             return;
32941         }
32942         
32943         if(this.bgimage.length && this.html.length){
32944             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32945         }
32946     },
32947     
32948     leave: function(e, el)
32949     {
32950         e.preventDefault();
32951         
32952         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32953             return;
32954         }
32955         
32956         if(this.bgimage.length && this.html.length){
32957             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32958         }
32959     },
32960     
32961     onTouchStart: function(e, el)
32962     {
32963 //        e.preventDefault();
32964         
32965         this.touchmoved = false;
32966         
32967         if(!this.isFitContainer){
32968             return;
32969         }
32970         
32971         if(!this.bgimage.length || !this.html.length){
32972             return;
32973         }
32974         
32975         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32976         
32977         this.timer = new Date().getTime();
32978         
32979     },
32980     
32981     onTouchMove: function(e, el)
32982     {
32983         this.touchmoved = true;
32984     },
32985     
32986     onContextMenu : function(e,el)
32987     {
32988         e.preventDefault();
32989         e.stopPropagation();
32990         return false;
32991     },
32992     
32993     onTouchEnd: function(e, el)
32994     {
32995 //        e.preventDefault();
32996         
32997         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32998         
32999             this.leave(e,el);
33000             
33001             return;
33002         }
33003         
33004         if(!this.bgimage.length || !this.html.length){
33005             
33006             if(this.href.length){
33007                 window.location.href = this.href;
33008             }
33009             
33010             return;
33011         }
33012         
33013         if(!this.isFitContainer){
33014             return;
33015         }
33016         
33017         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33018         
33019         window.location.href = this.href;
33020     },
33021     
33022     //selection on single brick only
33023     selectBrick : function() {
33024         
33025         if (!this.parentId) {
33026             return;
33027         }
33028         
33029         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33030         var index = m.selectedBrick.indexOf(this.id);
33031         
33032         if ( index > -1) {
33033             m.selectedBrick.splice(index,1);
33034             this.el.removeClass(this.activeClass);
33035             return;
33036         }
33037         
33038         for(var i = 0; i < m.selectedBrick.length; i++) {
33039             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33040             b.el.removeClass(b.activeClass);
33041         }
33042         
33043         m.selectedBrick = [];
33044         
33045         m.selectedBrick.push(this.id);
33046         this.el.addClass(this.activeClass);
33047         return;
33048     },
33049     
33050     isSelected : function(){
33051         return this.el.hasClass(this.activeClass);
33052         
33053     }
33054 });
33055
33056 Roo.apply(Roo.bootstrap.MasonryBrick, {
33057     
33058     //groups: {},
33059     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33060      /**
33061     * register a Masonry Brick
33062     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33063     */
33064     
33065     register : function(brick)
33066     {
33067         //this.groups[brick.id] = brick;
33068         this.groups.add(brick.id, brick);
33069     },
33070     /**
33071     * fetch a  masonry brick based on the masonry brick ID
33072     * @param {string} the masonry brick to add
33073     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33074     */
33075     
33076     get: function(brick_id) 
33077     {
33078         // if (typeof(this.groups[brick_id]) == 'undefined') {
33079         //     return false;
33080         // }
33081         // return this.groups[brick_id] ;
33082         
33083         if(this.groups.key(brick_id)) {
33084             return this.groups.key(brick_id);
33085         }
33086         
33087         return false;
33088     }
33089     
33090     
33091     
33092 });
33093
33094  /*
33095  * - LGPL
33096  *
33097  * element
33098  * 
33099  */
33100
33101 /**
33102  * @class Roo.bootstrap.Brick
33103  * @extends Roo.bootstrap.Component
33104  * Bootstrap Brick class
33105  * 
33106  * @constructor
33107  * Create a new Brick
33108  * @param {Object} config The config object
33109  */
33110
33111 Roo.bootstrap.Brick = function(config){
33112     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33113     
33114     this.addEvents({
33115         // raw events
33116         /**
33117          * @event click
33118          * When a Brick is click
33119          * @param {Roo.bootstrap.Brick} this
33120          * @param {Roo.EventObject} e
33121          */
33122         "click" : true
33123     });
33124 };
33125
33126 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33127     
33128     /**
33129      * @cfg {String} title
33130      */   
33131     title : '',
33132     /**
33133      * @cfg {String} html
33134      */   
33135     html : '',
33136     /**
33137      * @cfg {String} bgimage
33138      */   
33139     bgimage : '',
33140     /**
33141      * @cfg {String} cls
33142      */   
33143     cls : '',
33144     /**
33145      * @cfg {String} href
33146      */   
33147     href : '',
33148     /**
33149      * @cfg {String} video
33150      */   
33151     video : '',
33152     /**
33153      * @cfg {Boolean} square
33154      */   
33155     square : true,
33156     
33157     getAutoCreate : function()
33158     {
33159         var cls = 'roo-brick';
33160         
33161         if(this.href.length){
33162             cls += ' roo-brick-link';
33163         }
33164         
33165         if(this.bgimage.length){
33166             cls += ' roo-brick-image';
33167         }
33168         
33169         if(!this.html.length && !this.bgimage.length){
33170             cls += ' roo-brick-center-title';
33171         }
33172         
33173         if(!this.html.length && this.bgimage.length){
33174             cls += ' roo-brick-bottom-title';
33175         }
33176         
33177         if(this.cls){
33178             cls += ' ' + this.cls;
33179         }
33180         
33181         var cfg = {
33182             tag: (this.href.length) ? 'a' : 'div',
33183             cls: cls,
33184             cn: [
33185                 {
33186                     tag: 'div',
33187                     cls: 'roo-brick-paragraph',
33188                     cn: []
33189                 }
33190             ]
33191         };
33192         
33193         if(this.href.length){
33194             cfg.href = this.href;
33195         }
33196         
33197         var cn = cfg.cn[0].cn;
33198         
33199         if(this.title.length){
33200             cn.push({
33201                 tag: 'h4',
33202                 cls: 'roo-brick-title',
33203                 html: this.title
33204             });
33205         }
33206         
33207         if(this.html.length){
33208             cn.push({
33209                 tag: 'p',
33210                 cls: 'roo-brick-text',
33211                 html: this.html
33212             });
33213         } else {
33214             cn.cls += ' hide';
33215         }
33216         
33217         if(this.bgimage.length){
33218             cfg.cn.push({
33219                 tag: 'img',
33220                 cls: 'roo-brick-image-view',
33221                 src: this.bgimage
33222             });
33223         }
33224         
33225         return cfg;
33226     },
33227     
33228     initEvents: function() 
33229     {
33230         if(this.title.length || this.html.length){
33231             this.el.on('mouseenter'  ,this.enter, this);
33232             this.el.on('mouseleave', this.leave, this);
33233         }
33234         
33235         Roo.EventManager.onWindowResize(this.resize, this); 
33236         
33237         if(this.bgimage.length){
33238             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33239             this.imageEl.on('load', this.onImageLoad, this);
33240             return;
33241         }
33242         
33243         this.resize();
33244     },
33245     
33246     onImageLoad : function()
33247     {
33248         this.resize();
33249     },
33250     
33251     resize : function()
33252     {
33253         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33254         
33255         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33256         
33257         if(this.bgimage.length){
33258             var image = this.el.select('.roo-brick-image-view', true).first();
33259             
33260             image.setWidth(paragraph.getWidth());
33261             
33262             if(this.square){
33263                 image.setHeight(paragraph.getWidth());
33264             }
33265             
33266             this.el.setHeight(image.getHeight());
33267             paragraph.setHeight(image.getHeight());
33268             
33269         }
33270         
33271     },
33272     
33273     enter: function(e, el)
33274     {
33275         e.preventDefault();
33276         
33277         if(this.bgimage.length){
33278             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33279             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33280         }
33281     },
33282     
33283     leave: function(e, el)
33284     {
33285         e.preventDefault();
33286         
33287         if(this.bgimage.length){
33288             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33289             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33290         }
33291     }
33292     
33293 });
33294
33295  
33296
33297  /*
33298  * - LGPL
33299  *
33300  * Number field 
33301  */
33302
33303 /**
33304  * @class Roo.bootstrap.NumberField
33305  * @extends Roo.bootstrap.Input
33306  * Bootstrap NumberField class
33307  * 
33308  * 
33309  * 
33310  * 
33311  * @constructor
33312  * Create a new NumberField
33313  * @param {Object} config The config object
33314  */
33315
33316 Roo.bootstrap.NumberField = function(config){
33317     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33318 };
33319
33320 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33321     
33322     /**
33323      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33324      */
33325     allowDecimals : true,
33326     /**
33327      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33328      */
33329     decimalSeparator : ".",
33330     /**
33331      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33332      */
33333     decimalPrecision : 2,
33334     /**
33335      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33336      */
33337     allowNegative : true,
33338     
33339     /**
33340      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33341      */
33342     allowZero: true,
33343     /**
33344      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33345      */
33346     minValue : Number.NEGATIVE_INFINITY,
33347     /**
33348      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33349      */
33350     maxValue : Number.MAX_VALUE,
33351     /**
33352      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33353      */
33354     minText : "The minimum value for this field is {0}",
33355     /**
33356      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33357      */
33358     maxText : "The maximum value for this field is {0}",
33359     /**
33360      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33361      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33362      */
33363     nanText : "{0} is not a valid number",
33364     /**
33365      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33366      */
33367     thousandsDelimiter : false,
33368     /**
33369      * @cfg {String} valueAlign alignment of value
33370      */
33371     valueAlign : "left",
33372
33373     getAutoCreate : function()
33374     {
33375         var hiddenInput = {
33376             tag: 'input',
33377             type: 'hidden',
33378             id: Roo.id(),
33379             cls: 'hidden-number-input'
33380         };
33381         
33382         if (this.name) {
33383             hiddenInput.name = this.name;
33384         }
33385         
33386         this.name = '';
33387         
33388         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33389         
33390         this.name = hiddenInput.name;
33391         
33392         if(cfg.cn.length > 0) {
33393             cfg.cn.push(hiddenInput);
33394         }
33395         
33396         return cfg;
33397     },
33398
33399     // private
33400     initEvents : function()
33401     {   
33402         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33403         
33404         var allowed = "0123456789";
33405         
33406         if(this.allowDecimals){
33407             allowed += this.decimalSeparator;
33408         }
33409         
33410         if(this.allowNegative){
33411             allowed += "-";
33412         }
33413         
33414         if(this.thousandsDelimiter) {
33415             allowed += ",";
33416         }
33417         
33418         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33419         
33420         var keyPress = function(e){
33421             
33422             var k = e.getKey();
33423             
33424             var c = e.getCharCode();
33425             
33426             if(
33427                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33428                     allowed.indexOf(String.fromCharCode(c)) === -1
33429             ){
33430                 e.stopEvent();
33431                 return;
33432             }
33433             
33434             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33435                 return;
33436             }
33437             
33438             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33439                 e.stopEvent();
33440             }
33441         };
33442         
33443         this.el.on("keypress", keyPress, this);
33444     },
33445     
33446     validateValue : function(value)
33447     {
33448         
33449         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33450             return false;
33451         }
33452         
33453         var num = this.parseValue(value);
33454         
33455         if(isNaN(num)){
33456             this.markInvalid(String.format(this.nanText, value));
33457             return false;
33458         }
33459         
33460         if(num < this.minValue){
33461             this.markInvalid(String.format(this.minText, this.minValue));
33462             return false;
33463         }
33464         
33465         if(num > this.maxValue){
33466             this.markInvalid(String.format(this.maxText, this.maxValue));
33467             return false;
33468         }
33469         
33470         return true;
33471     },
33472
33473     getValue : function()
33474     {
33475         var v = this.hiddenEl().getValue();
33476         
33477         return this.fixPrecision(this.parseValue(v));
33478     },
33479
33480     parseValue : function(value)
33481     {
33482         if(this.thousandsDelimiter) {
33483             value += "";
33484             r = new RegExp(",", "g");
33485             value = value.replace(r, "");
33486         }
33487         
33488         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33489         return isNaN(value) ? '' : value;
33490     },
33491
33492     fixPrecision : function(value)
33493     {
33494         if(this.thousandsDelimiter) {
33495             value += "";
33496             r = new RegExp(",", "g");
33497             value = value.replace(r, "");
33498         }
33499         
33500         var nan = isNaN(value);
33501         
33502         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33503             return nan ? '' : value;
33504         }
33505         return parseFloat(value).toFixed(this.decimalPrecision);
33506     },
33507
33508     setValue : function(v)
33509     {
33510         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33511         
33512         this.value = v;
33513         
33514         if(this.rendered){
33515             
33516             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33517             
33518             this.inputEl().dom.value = (v == '') ? '' :
33519                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33520             
33521             if(!this.allowZero && v === '0') {
33522                 this.hiddenEl().dom.value = '';
33523                 this.inputEl().dom.value = '';
33524             }
33525             
33526             this.validate();
33527         }
33528     },
33529
33530     decimalPrecisionFcn : function(v)
33531     {
33532         return Math.floor(v);
33533     },
33534
33535     beforeBlur : function()
33536     {
33537         var v = this.parseValue(this.getRawValue());
33538         
33539         if(v || v === 0 || v === ''){
33540             this.setValue(v);
33541         }
33542     },
33543     
33544     hiddenEl : function()
33545     {
33546         return this.el.select('input.hidden-number-input',true).first();
33547     }
33548     
33549 });
33550
33551  
33552
33553 /*
33554 * Licence: LGPL
33555 */
33556
33557 /**
33558  * @class Roo.bootstrap.DocumentSlider
33559  * @extends Roo.bootstrap.Component
33560  * Bootstrap DocumentSlider class
33561  * 
33562  * @constructor
33563  * Create a new DocumentViewer
33564  * @param {Object} config The config object
33565  */
33566
33567 Roo.bootstrap.DocumentSlider = function(config){
33568     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33569     
33570     this.files = [];
33571     
33572     this.addEvents({
33573         /**
33574          * @event initial
33575          * Fire after initEvent
33576          * @param {Roo.bootstrap.DocumentSlider} this
33577          */
33578         "initial" : true,
33579         /**
33580          * @event update
33581          * Fire after update
33582          * @param {Roo.bootstrap.DocumentSlider} this
33583          */
33584         "update" : true,
33585         /**
33586          * @event click
33587          * Fire after click
33588          * @param {Roo.bootstrap.DocumentSlider} this
33589          */
33590         "click" : true
33591     });
33592 };
33593
33594 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33595     
33596     files : false,
33597     
33598     indicator : 0,
33599     
33600     getAutoCreate : function()
33601     {
33602         var cfg = {
33603             tag : 'div',
33604             cls : 'roo-document-slider',
33605             cn : [
33606                 {
33607                     tag : 'div',
33608                     cls : 'roo-document-slider-header',
33609                     cn : [
33610                         {
33611                             tag : 'div',
33612                             cls : 'roo-document-slider-header-title'
33613                         }
33614                     ]
33615                 },
33616                 {
33617                     tag : 'div',
33618                     cls : 'roo-document-slider-body',
33619                     cn : [
33620                         {
33621                             tag : 'div',
33622                             cls : 'roo-document-slider-prev',
33623                             cn : [
33624                                 {
33625                                     tag : 'i',
33626                                     cls : 'fa fa-chevron-left'
33627                                 }
33628                             ]
33629                         },
33630                         {
33631                             tag : 'div',
33632                             cls : 'roo-document-slider-thumb',
33633                             cn : [
33634                                 {
33635                                     tag : 'img',
33636                                     cls : 'roo-document-slider-image'
33637                                 }
33638                             ]
33639                         },
33640                         {
33641                             tag : 'div',
33642                             cls : 'roo-document-slider-next',
33643                             cn : [
33644                                 {
33645                                     tag : 'i',
33646                                     cls : 'fa fa-chevron-right'
33647                                 }
33648                             ]
33649                         }
33650                     ]
33651                 }
33652             ]
33653         };
33654         
33655         return cfg;
33656     },
33657     
33658     initEvents : function()
33659     {
33660         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33661         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33662         
33663         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33664         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33665         
33666         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33667         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33668         
33669         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33670         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33671         
33672         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33673         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33674         
33675         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33676         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33677         
33678         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33679         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33680         
33681         this.thumbEl.on('click', this.onClick, this);
33682         
33683         this.prevIndicator.on('click', this.prev, this);
33684         
33685         this.nextIndicator.on('click', this.next, this);
33686         
33687     },
33688     
33689     initial : function()
33690     {
33691         if(this.files.length){
33692             this.indicator = 1;
33693             this.update()
33694         }
33695         
33696         this.fireEvent('initial', this);
33697     },
33698     
33699     update : function()
33700     {
33701         this.imageEl.attr('src', this.files[this.indicator - 1]);
33702         
33703         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33704         
33705         this.prevIndicator.show();
33706         
33707         if(this.indicator == 1){
33708             this.prevIndicator.hide();
33709         }
33710         
33711         this.nextIndicator.show();
33712         
33713         if(this.indicator == this.files.length){
33714             this.nextIndicator.hide();
33715         }
33716         
33717         this.thumbEl.scrollTo('top');
33718         
33719         this.fireEvent('update', this);
33720     },
33721     
33722     onClick : function(e)
33723     {
33724         e.preventDefault();
33725         
33726         this.fireEvent('click', this);
33727     },
33728     
33729     prev : function(e)
33730     {
33731         e.preventDefault();
33732         
33733         this.indicator = Math.max(1, this.indicator - 1);
33734         
33735         this.update();
33736     },
33737     
33738     next : function(e)
33739     {
33740         e.preventDefault();
33741         
33742         this.indicator = Math.min(this.files.length, this.indicator + 1);
33743         
33744         this.update();
33745     }
33746 });
33747 /*
33748  * - LGPL
33749  *
33750  * RadioSet
33751  *
33752  *
33753  */
33754
33755 /**
33756  * @class Roo.bootstrap.RadioSet
33757  * @extends Roo.bootstrap.Input
33758  * Bootstrap RadioSet class
33759  * @cfg {String} indicatorpos (left|right) default left
33760  * @cfg {Boolean} inline (true|false) inline the element (default true)
33761  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33762  * @constructor
33763  * Create a new RadioSet
33764  * @param {Object} config The config object
33765  */
33766
33767 Roo.bootstrap.RadioSet = function(config){
33768     
33769     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33770     
33771     this.radioes = [];
33772     
33773     Roo.bootstrap.RadioSet.register(this);
33774     
33775     this.addEvents({
33776         /**
33777         * @event check
33778         * Fires when the element is checked or unchecked.
33779         * @param {Roo.bootstrap.RadioSet} this This radio
33780         * @param {Roo.bootstrap.Radio} item The checked item
33781         */
33782        check : true,
33783        /**
33784         * @event click
33785         * Fires when the element is click.
33786         * @param {Roo.bootstrap.RadioSet} this This radio set
33787         * @param {Roo.bootstrap.Radio} item The checked item
33788         * @param {Roo.EventObject} e The event object
33789         */
33790        click : true
33791     });
33792     
33793 };
33794
33795 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33796
33797     radioes : false,
33798     
33799     inline : true,
33800     
33801     weight : '',
33802     
33803     indicatorpos : 'left',
33804     
33805     getAutoCreate : function()
33806     {
33807         var label = {
33808             tag : 'label',
33809             cls : 'roo-radio-set-label',
33810             cn : [
33811                 {
33812                     tag : 'span',
33813                     html : this.fieldLabel
33814                 }
33815             ]
33816         };
33817         
33818         if(this.indicatorpos == 'left'){
33819             label.cn.unshift({
33820                 tag : 'i',
33821                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33822                 tooltip : 'This field is required'
33823             });
33824         } else {
33825             label.cn.push({
33826                 tag : 'i',
33827                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33828                 tooltip : 'This field is required'
33829             });
33830         }
33831         
33832         var items = {
33833             tag : 'div',
33834             cls : 'roo-radio-set-items'
33835         };
33836         
33837         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33838         
33839         if (align === 'left' && this.fieldLabel.length) {
33840             
33841             items = {
33842                 cls : "roo-radio-set-right", 
33843                 cn: [
33844                     items
33845                 ]
33846             };
33847             
33848             if(this.labelWidth > 12){
33849                 label.style = "width: " + this.labelWidth + 'px';
33850             }
33851             
33852             if(this.labelWidth < 13 && this.labelmd == 0){
33853                 this.labelmd = this.labelWidth;
33854             }
33855             
33856             if(this.labellg > 0){
33857                 label.cls += ' col-lg-' + this.labellg;
33858                 items.cls += ' col-lg-' + (12 - this.labellg);
33859             }
33860             
33861             if(this.labelmd > 0){
33862                 label.cls += ' col-md-' + this.labelmd;
33863                 items.cls += ' col-md-' + (12 - this.labelmd);
33864             }
33865             
33866             if(this.labelsm > 0){
33867                 label.cls += ' col-sm-' + this.labelsm;
33868                 items.cls += ' col-sm-' + (12 - this.labelsm);
33869             }
33870             
33871             if(this.labelxs > 0){
33872                 label.cls += ' col-xs-' + this.labelxs;
33873                 items.cls += ' col-xs-' + (12 - this.labelxs);
33874             }
33875         }
33876         
33877         var cfg = {
33878             tag : 'div',
33879             cls : 'roo-radio-set',
33880             cn : [
33881                 {
33882                     tag : 'input',
33883                     cls : 'roo-radio-set-input',
33884                     type : 'hidden',
33885                     name : this.name,
33886                     value : this.value ? this.value :  ''
33887                 },
33888                 label,
33889                 items
33890             ]
33891         };
33892         
33893         if(this.weight.length){
33894             cfg.cls += ' roo-radio-' + this.weight;
33895         }
33896         
33897         if(this.inline) {
33898             cfg.cls += ' roo-radio-set-inline';
33899         }
33900         
33901         var settings=this;
33902         ['xs','sm','md','lg'].map(function(size){
33903             if (settings[size]) {
33904                 cfg.cls += ' col-' + size + '-' + settings[size];
33905             }
33906         });
33907         
33908         return cfg;
33909         
33910     },
33911
33912     initEvents : function()
33913     {
33914         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33915         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33916         
33917         if(!this.fieldLabel.length){
33918             this.labelEl.hide();
33919         }
33920         
33921         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33922         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33923         
33924         this.indicator = this.indicatorEl();
33925         
33926         if(this.indicator){
33927             this.indicator.addClass('invisible');
33928         }
33929         
33930         this.originalValue = this.getValue();
33931         
33932     },
33933     
33934     inputEl: function ()
33935     {
33936         return this.el.select('.roo-radio-set-input', true).first();
33937     },
33938     
33939     getChildContainer : function()
33940     {
33941         return this.itemsEl;
33942     },
33943     
33944     register : function(item)
33945     {
33946         this.radioes.push(item);
33947         
33948     },
33949     
33950     validate : function()
33951     {   
33952         if(this.getVisibilityEl().hasClass('hidden')){
33953             return true;
33954         }
33955         
33956         var valid = false;
33957         
33958         Roo.each(this.radioes, function(i){
33959             if(!i.checked){
33960                 return;
33961             }
33962             
33963             valid = true;
33964             return false;
33965         });
33966         
33967         if(this.allowBlank) {
33968             return true;
33969         }
33970         
33971         if(this.disabled || valid){
33972             this.markValid();
33973             return true;
33974         }
33975         
33976         this.markInvalid();
33977         return false;
33978         
33979     },
33980     
33981     markValid : function()
33982     {
33983         if(this.labelEl.isVisible(true)){
33984             this.indicatorEl().removeClass('visible');
33985             this.indicatorEl().addClass('invisible');
33986         }
33987         
33988         this.el.removeClass([this.invalidClass, this.validClass]);
33989         this.el.addClass(this.validClass);
33990         
33991         this.fireEvent('valid', this);
33992     },
33993     
33994     markInvalid : function(msg)
33995     {
33996         if(this.allowBlank || this.disabled){
33997             return;
33998         }
33999         
34000         if(this.labelEl.isVisible(true)){
34001             this.indicatorEl().removeClass('invisible');
34002             this.indicatorEl().addClass('visible');
34003         }
34004         
34005         this.el.removeClass([this.invalidClass, this.validClass]);
34006         this.el.addClass(this.invalidClass);
34007         
34008         this.fireEvent('invalid', this, msg);
34009         
34010     },
34011     
34012     setValue : function(v, suppressEvent)
34013     {   
34014         if(this.value === v){
34015             return;
34016         }
34017         
34018         this.value = v;
34019         
34020         if(this.rendered){
34021             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34022         }
34023         
34024         Roo.each(this.radioes, function(i){
34025             i.checked = false;
34026             i.el.removeClass('checked');
34027         });
34028         
34029         Roo.each(this.radioes, function(i){
34030             
34031             if(i.value === v || i.value.toString() === v.toString()){
34032                 i.checked = true;
34033                 i.el.addClass('checked');
34034                 
34035                 if(suppressEvent !== true){
34036                     this.fireEvent('check', this, i);
34037                 }
34038                 
34039                 return false;
34040             }
34041             
34042         }, this);
34043         
34044         this.validate();
34045     },
34046     
34047     clearInvalid : function(){
34048         
34049         if(!this.el || this.preventMark){
34050             return;
34051         }
34052         
34053         this.el.removeClass([this.invalidClass]);
34054         
34055         this.fireEvent('valid', this);
34056     }
34057     
34058 });
34059
34060 Roo.apply(Roo.bootstrap.RadioSet, {
34061     
34062     groups: {},
34063     
34064     register : function(set)
34065     {
34066         this.groups[set.name] = set;
34067     },
34068     
34069     get: function(name) 
34070     {
34071         if (typeof(this.groups[name]) == 'undefined') {
34072             return false;
34073         }
34074         
34075         return this.groups[name] ;
34076     }
34077     
34078 });
34079 /*
34080  * Based on:
34081  * Ext JS Library 1.1.1
34082  * Copyright(c) 2006-2007, Ext JS, LLC.
34083  *
34084  * Originally Released Under LGPL - original licence link has changed is not relivant.
34085  *
34086  * Fork - LGPL
34087  * <script type="text/javascript">
34088  */
34089
34090
34091 /**
34092  * @class Roo.bootstrap.SplitBar
34093  * @extends Roo.util.Observable
34094  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34095  * <br><br>
34096  * Usage:
34097  * <pre><code>
34098 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34099                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34100 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34101 split.minSize = 100;
34102 split.maxSize = 600;
34103 split.animate = true;
34104 split.on('moved', splitterMoved);
34105 </code></pre>
34106  * @constructor
34107  * Create a new SplitBar
34108  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34109  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34110  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34111  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34112                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34113                         position of the SplitBar).
34114  */
34115 Roo.bootstrap.SplitBar = function(cfg){
34116     
34117     /** @private */
34118     
34119     //{
34120     //  dragElement : elm
34121     //  resizingElement: el,
34122         // optional..
34123     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34124     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34125         // existingProxy ???
34126     //}
34127     
34128     this.el = Roo.get(cfg.dragElement, true);
34129     this.el.dom.unselectable = "on";
34130     /** @private */
34131     this.resizingEl = Roo.get(cfg.resizingElement, true);
34132
34133     /**
34134      * @private
34135      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34136      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34137      * @type Number
34138      */
34139     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34140     
34141     /**
34142      * The minimum size of the resizing element. (Defaults to 0)
34143      * @type Number
34144      */
34145     this.minSize = 0;
34146     
34147     /**
34148      * The maximum size of the resizing element. (Defaults to 2000)
34149      * @type Number
34150      */
34151     this.maxSize = 2000;
34152     
34153     /**
34154      * Whether to animate the transition to the new size
34155      * @type Boolean
34156      */
34157     this.animate = false;
34158     
34159     /**
34160      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34161      * @type Boolean
34162      */
34163     this.useShim = false;
34164     
34165     /** @private */
34166     this.shim = null;
34167     
34168     if(!cfg.existingProxy){
34169         /** @private */
34170         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34171     }else{
34172         this.proxy = Roo.get(cfg.existingProxy).dom;
34173     }
34174     /** @private */
34175     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34176     
34177     /** @private */
34178     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34179     
34180     /** @private */
34181     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34182     
34183     /** @private */
34184     this.dragSpecs = {};
34185     
34186     /**
34187      * @private The adapter to use to positon and resize elements
34188      */
34189     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34190     this.adapter.init(this);
34191     
34192     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34193         /** @private */
34194         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34195         this.el.addClass("roo-splitbar-h");
34196     }else{
34197         /** @private */
34198         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34199         this.el.addClass("roo-splitbar-v");
34200     }
34201     
34202     this.addEvents({
34203         /**
34204          * @event resize
34205          * Fires when the splitter is moved (alias for {@link #event-moved})
34206          * @param {Roo.bootstrap.SplitBar} this
34207          * @param {Number} newSize the new width or height
34208          */
34209         "resize" : true,
34210         /**
34211          * @event moved
34212          * Fires when the splitter is moved
34213          * @param {Roo.bootstrap.SplitBar} this
34214          * @param {Number} newSize the new width or height
34215          */
34216         "moved" : true,
34217         /**
34218          * @event beforeresize
34219          * Fires before the splitter is dragged
34220          * @param {Roo.bootstrap.SplitBar} this
34221          */
34222         "beforeresize" : true,
34223
34224         "beforeapply" : true
34225     });
34226
34227     Roo.util.Observable.call(this);
34228 };
34229
34230 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34231     onStartProxyDrag : function(x, y){
34232         this.fireEvent("beforeresize", this);
34233         if(!this.overlay){
34234             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34235             o.unselectable();
34236             o.enableDisplayMode("block");
34237             // all splitbars share the same overlay
34238             Roo.bootstrap.SplitBar.prototype.overlay = o;
34239         }
34240         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34241         this.overlay.show();
34242         Roo.get(this.proxy).setDisplayed("block");
34243         var size = this.adapter.getElementSize(this);
34244         this.activeMinSize = this.getMinimumSize();;
34245         this.activeMaxSize = this.getMaximumSize();;
34246         var c1 = size - this.activeMinSize;
34247         var c2 = Math.max(this.activeMaxSize - size, 0);
34248         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34249             this.dd.resetConstraints();
34250             this.dd.setXConstraint(
34251                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34252                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34253             );
34254             this.dd.setYConstraint(0, 0);
34255         }else{
34256             this.dd.resetConstraints();
34257             this.dd.setXConstraint(0, 0);
34258             this.dd.setYConstraint(
34259                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34260                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34261             );
34262          }
34263         this.dragSpecs.startSize = size;
34264         this.dragSpecs.startPoint = [x, y];
34265         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34266     },
34267     
34268     /** 
34269      * @private Called after the drag operation by the DDProxy
34270      */
34271     onEndProxyDrag : function(e){
34272         Roo.get(this.proxy).setDisplayed(false);
34273         var endPoint = Roo.lib.Event.getXY(e);
34274         if(this.overlay){
34275             this.overlay.hide();
34276         }
34277         var newSize;
34278         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34279             newSize = this.dragSpecs.startSize + 
34280                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34281                     endPoint[0] - this.dragSpecs.startPoint[0] :
34282                     this.dragSpecs.startPoint[0] - endPoint[0]
34283                 );
34284         }else{
34285             newSize = this.dragSpecs.startSize + 
34286                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34287                     endPoint[1] - this.dragSpecs.startPoint[1] :
34288                     this.dragSpecs.startPoint[1] - endPoint[1]
34289                 );
34290         }
34291         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34292         if(newSize != this.dragSpecs.startSize){
34293             if(this.fireEvent('beforeapply', this, newSize) !== false){
34294                 this.adapter.setElementSize(this, newSize);
34295                 this.fireEvent("moved", this, newSize);
34296                 this.fireEvent("resize", this, newSize);
34297             }
34298         }
34299     },
34300     
34301     /**
34302      * Get the adapter this SplitBar uses
34303      * @return The adapter object
34304      */
34305     getAdapter : function(){
34306         return this.adapter;
34307     },
34308     
34309     /**
34310      * Set the adapter this SplitBar uses
34311      * @param {Object} adapter A SplitBar adapter object
34312      */
34313     setAdapter : function(adapter){
34314         this.adapter = adapter;
34315         this.adapter.init(this);
34316     },
34317     
34318     /**
34319      * Gets the minimum size for the resizing element
34320      * @return {Number} The minimum size
34321      */
34322     getMinimumSize : function(){
34323         return this.minSize;
34324     },
34325     
34326     /**
34327      * Sets the minimum size for the resizing element
34328      * @param {Number} minSize The minimum size
34329      */
34330     setMinimumSize : function(minSize){
34331         this.minSize = minSize;
34332     },
34333     
34334     /**
34335      * Gets the maximum size for the resizing element
34336      * @return {Number} The maximum size
34337      */
34338     getMaximumSize : function(){
34339         return this.maxSize;
34340     },
34341     
34342     /**
34343      * Sets the maximum size for the resizing element
34344      * @param {Number} maxSize The maximum size
34345      */
34346     setMaximumSize : function(maxSize){
34347         this.maxSize = maxSize;
34348     },
34349     
34350     /**
34351      * Sets the initialize size for the resizing element
34352      * @param {Number} size The initial size
34353      */
34354     setCurrentSize : function(size){
34355         var oldAnimate = this.animate;
34356         this.animate = false;
34357         this.adapter.setElementSize(this, size);
34358         this.animate = oldAnimate;
34359     },
34360     
34361     /**
34362      * Destroy this splitbar. 
34363      * @param {Boolean} removeEl True to remove the element
34364      */
34365     destroy : function(removeEl){
34366         if(this.shim){
34367             this.shim.remove();
34368         }
34369         this.dd.unreg();
34370         this.proxy.parentNode.removeChild(this.proxy);
34371         if(removeEl){
34372             this.el.remove();
34373         }
34374     }
34375 });
34376
34377 /**
34378  * @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.
34379  */
34380 Roo.bootstrap.SplitBar.createProxy = function(dir){
34381     var proxy = new Roo.Element(document.createElement("div"));
34382     proxy.unselectable();
34383     var cls = 'roo-splitbar-proxy';
34384     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34385     document.body.appendChild(proxy.dom);
34386     return proxy.dom;
34387 };
34388
34389 /** 
34390  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34391  * Default Adapter. It assumes the splitter and resizing element are not positioned
34392  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34393  */
34394 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34395 };
34396
34397 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34398     // do nothing for now
34399     init : function(s){
34400     
34401     },
34402     /**
34403      * Called before drag operations to get the current size of the resizing element. 
34404      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34405      */
34406      getElementSize : function(s){
34407         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34408             return s.resizingEl.getWidth();
34409         }else{
34410             return s.resizingEl.getHeight();
34411         }
34412     },
34413     
34414     /**
34415      * Called after drag operations to set the size of the resizing element.
34416      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34417      * @param {Number} newSize The new size to set
34418      * @param {Function} onComplete A function to be invoked when resizing is complete
34419      */
34420     setElementSize : function(s, newSize, onComplete){
34421         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34422             if(!s.animate){
34423                 s.resizingEl.setWidth(newSize);
34424                 if(onComplete){
34425                     onComplete(s, newSize);
34426                 }
34427             }else{
34428                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34429             }
34430         }else{
34431             
34432             if(!s.animate){
34433                 s.resizingEl.setHeight(newSize);
34434                 if(onComplete){
34435                     onComplete(s, newSize);
34436                 }
34437             }else{
34438                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34439             }
34440         }
34441     }
34442 };
34443
34444 /** 
34445  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34446  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34447  * Adapter that  moves the splitter element to align with the resized sizing element. 
34448  * Used with an absolute positioned SplitBar.
34449  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34450  * document.body, make sure you assign an id to the body element.
34451  */
34452 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34453     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34454     this.container = Roo.get(container);
34455 };
34456
34457 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34458     init : function(s){
34459         this.basic.init(s);
34460     },
34461     
34462     getElementSize : function(s){
34463         return this.basic.getElementSize(s);
34464     },
34465     
34466     setElementSize : function(s, newSize, onComplete){
34467         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34468     },
34469     
34470     moveSplitter : function(s){
34471         var yes = Roo.bootstrap.SplitBar;
34472         switch(s.placement){
34473             case yes.LEFT:
34474                 s.el.setX(s.resizingEl.getRight());
34475                 break;
34476             case yes.RIGHT:
34477                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34478                 break;
34479             case yes.TOP:
34480                 s.el.setY(s.resizingEl.getBottom());
34481                 break;
34482             case yes.BOTTOM:
34483                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34484                 break;
34485         }
34486     }
34487 };
34488
34489 /**
34490  * Orientation constant - Create a vertical SplitBar
34491  * @static
34492  * @type Number
34493  */
34494 Roo.bootstrap.SplitBar.VERTICAL = 1;
34495
34496 /**
34497  * Orientation constant - Create a horizontal SplitBar
34498  * @static
34499  * @type Number
34500  */
34501 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34502
34503 /**
34504  * Placement constant - The resizing element is to the left of the splitter element
34505  * @static
34506  * @type Number
34507  */
34508 Roo.bootstrap.SplitBar.LEFT = 1;
34509
34510 /**
34511  * Placement constant - The resizing element is to the right of the splitter element
34512  * @static
34513  * @type Number
34514  */
34515 Roo.bootstrap.SplitBar.RIGHT = 2;
34516
34517 /**
34518  * Placement constant - The resizing element is positioned above the splitter element
34519  * @static
34520  * @type Number
34521  */
34522 Roo.bootstrap.SplitBar.TOP = 3;
34523
34524 /**
34525  * Placement constant - The resizing element is positioned under splitter element
34526  * @static
34527  * @type Number
34528  */
34529 Roo.bootstrap.SplitBar.BOTTOM = 4;
34530 Roo.namespace("Roo.bootstrap.layout");/*
34531  * Based on:
34532  * Ext JS Library 1.1.1
34533  * Copyright(c) 2006-2007, Ext JS, LLC.
34534  *
34535  * Originally Released Under LGPL - original licence link has changed is not relivant.
34536  *
34537  * Fork - LGPL
34538  * <script type="text/javascript">
34539  */
34540
34541 /**
34542  * @class Roo.bootstrap.layout.Manager
34543  * @extends Roo.bootstrap.Component
34544  * Base class for layout managers.
34545  */
34546 Roo.bootstrap.layout.Manager = function(config)
34547 {
34548     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34549
34550
34551
34552
34553
34554     /** false to disable window resize monitoring @type Boolean */
34555     this.monitorWindowResize = true;
34556     this.regions = {};
34557     this.addEvents({
34558         /**
34559          * @event layout
34560          * Fires when a layout is performed.
34561          * @param {Roo.LayoutManager} this
34562          */
34563         "layout" : true,
34564         /**
34565          * @event regionresized
34566          * Fires when the user resizes a region.
34567          * @param {Roo.LayoutRegion} region The resized region
34568          * @param {Number} newSize The new size (width for east/west, height for north/south)
34569          */
34570         "regionresized" : true,
34571         /**
34572          * @event regioncollapsed
34573          * Fires when a region is collapsed.
34574          * @param {Roo.LayoutRegion} region The collapsed region
34575          */
34576         "regioncollapsed" : true,
34577         /**
34578          * @event regionexpanded
34579          * Fires when a region is expanded.
34580          * @param {Roo.LayoutRegion} region The expanded region
34581          */
34582         "regionexpanded" : true
34583     });
34584     this.updating = false;
34585
34586     if (config.el) {
34587         this.el = Roo.get(config.el);
34588         this.initEvents();
34589     }
34590
34591 };
34592
34593 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34594
34595
34596     regions : null,
34597
34598     monitorWindowResize : true,
34599
34600
34601     updating : false,
34602
34603
34604     onRender : function(ct, position)
34605     {
34606         if(!this.el){
34607             this.el = Roo.get(ct);
34608             this.initEvents();
34609         }
34610         //this.fireEvent('render',this);
34611     },
34612
34613
34614     initEvents: function()
34615     {
34616
34617
34618         // ie scrollbar fix
34619         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34620             document.body.scroll = "no";
34621         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34622             this.el.position('relative');
34623         }
34624         this.id = this.el.id;
34625         this.el.addClass("roo-layout-container");
34626         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34627         if(this.el.dom != document.body ) {
34628             this.el.on('resize', this.layout,this);
34629             this.el.on('show', this.layout,this);
34630         }
34631
34632     },
34633
34634     /**
34635      * Returns true if this layout is currently being updated
34636      * @return {Boolean}
34637      */
34638     isUpdating : function(){
34639         return this.updating;
34640     },
34641
34642     /**
34643      * Suspend the LayoutManager from doing auto-layouts while
34644      * making multiple add or remove calls
34645      */
34646     beginUpdate : function(){
34647         this.updating = true;
34648     },
34649
34650     /**
34651      * Restore auto-layouts and optionally disable the manager from performing a layout
34652      * @param {Boolean} noLayout true to disable a layout update
34653      */
34654     endUpdate : function(noLayout){
34655         this.updating = false;
34656         if(!noLayout){
34657             this.layout();
34658         }
34659     },
34660
34661     layout: function(){
34662         // abstract...
34663     },
34664
34665     onRegionResized : function(region, newSize){
34666         this.fireEvent("regionresized", region, newSize);
34667         this.layout();
34668     },
34669
34670     onRegionCollapsed : function(region){
34671         this.fireEvent("regioncollapsed", region);
34672     },
34673
34674     onRegionExpanded : function(region){
34675         this.fireEvent("regionexpanded", region);
34676     },
34677
34678     /**
34679      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34680      * performs box-model adjustments.
34681      * @return {Object} The size as an object {width: (the width), height: (the height)}
34682      */
34683     getViewSize : function()
34684     {
34685         var size;
34686         if(this.el.dom != document.body){
34687             size = this.el.getSize();
34688         }else{
34689             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34690         }
34691         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34692         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34693         return size;
34694     },
34695
34696     /**
34697      * Returns the Element this layout is bound to.
34698      * @return {Roo.Element}
34699      */
34700     getEl : function(){
34701         return this.el;
34702     },
34703
34704     /**
34705      * Returns the specified region.
34706      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34707      * @return {Roo.LayoutRegion}
34708      */
34709     getRegion : function(target){
34710         return this.regions[target.toLowerCase()];
34711     },
34712
34713     onWindowResize : function(){
34714         if(this.monitorWindowResize){
34715             this.layout();
34716         }
34717     }
34718 });
34719 /*
34720  * Based on:
34721  * Ext JS Library 1.1.1
34722  * Copyright(c) 2006-2007, Ext JS, LLC.
34723  *
34724  * Originally Released Under LGPL - original licence link has changed is not relivant.
34725  *
34726  * Fork - LGPL
34727  * <script type="text/javascript">
34728  */
34729 /**
34730  * @class Roo.bootstrap.layout.Border
34731  * @extends Roo.bootstrap.layout.Manager
34732  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34733  * please see: examples/bootstrap/nested.html<br><br>
34734  
34735 <b>The container the layout is rendered into can be either the body element or any other element.
34736 If it is not the body element, the container needs to either be an absolute positioned element,
34737 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34738 the container size if it is not the body element.</b>
34739
34740 * @constructor
34741 * Create a new Border
34742 * @param {Object} config Configuration options
34743  */
34744 Roo.bootstrap.layout.Border = function(config){
34745     config = config || {};
34746     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34747     
34748     
34749     
34750     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34751         if(config[region]){
34752             config[region].region = region;
34753             this.addRegion(config[region]);
34754         }
34755     },this);
34756     
34757 };
34758
34759 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34760
34761 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34762     /**
34763      * Creates and adds a new region if it doesn't already exist.
34764      * @param {String} target The target region key (north, south, east, west or center).
34765      * @param {Object} config The regions config object
34766      * @return {BorderLayoutRegion} The new region
34767      */
34768     addRegion : function(config)
34769     {
34770         if(!this.regions[config.region]){
34771             var r = this.factory(config);
34772             this.bindRegion(r);
34773         }
34774         return this.regions[config.region];
34775     },
34776
34777     // private (kinda)
34778     bindRegion : function(r){
34779         this.regions[r.config.region] = r;
34780         
34781         r.on("visibilitychange",    this.layout, this);
34782         r.on("paneladded",          this.layout, this);
34783         r.on("panelremoved",        this.layout, this);
34784         r.on("invalidated",         this.layout, this);
34785         r.on("resized",             this.onRegionResized, this);
34786         r.on("collapsed",           this.onRegionCollapsed, this);
34787         r.on("expanded",            this.onRegionExpanded, this);
34788     },
34789
34790     /**
34791      * Performs a layout update.
34792      */
34793     layout : function()
34794     {
34795         if(this.updating) {
34796             return;
34797         }
34798         
34799         // render all the rebions if they have not been done alreayd?
34800         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34801             if(this.regions[region] && !this.regions[region].bodyEl){
34802                 this.regions[region].onRender(this.el)
34803             }
34804         },this);
34805         
34806         var size = this.getViewSize();
34807         var w = size.width;
34808         var h = size.height;
34809         var centerW = w;
34810         var centerH = h;
34811         var centerY = 0;
34812         var centerX = 0;
34813         //var x = 0, y = 0;
34814
34815         var rs = this.regions;
34816         var north = rs["north"];
34817         var south = rs["south"]; 
34818         var west = rs["west"];
34819         var east = rs["east"];
34820         var center = rs["center"];
34821         //if(this.hideOnLayout){ // not supported anymore
34822             //c.el.setStyle("display", "none");
34823         //}
34824         if(north && north.isVisible()){
34825             var b = north.getBox();
34826             var m = north.getMargins();
34827             b.width = w - (m.left+m.right);
34828             b.x = m.left;
34829             b.y = m.top;
34830             centerY = b.height + b.y + m.bottom;
34831             centerH -= centerY;
34832             north.updateBox(this.safeBox(b));
34833         }
34834         if(south && south.isVisible()){
34835             var b = south.getBox();
34836             var m = south.getMargins();
34837             b.width = w - (m.left+m.right);
34838             b.x = m.left;
34839             var totalHeight = (b.height + m.top + m.bottom);
34840             b.y = h - totalHeight + m.top;
34841             centerH -= totalHeight;
34842             south.updateBox(this.safeBox(b));
34843         }
34844         if(west && west.isVisible()){
34845             var b = west.getBox();
34846             var m = west.getMargins();
34847             b.height = centerH - (m.top+m.bottom);
34848             b.x = m.left;
34849             b.y = centerY + m.top;
34850             var totalWidth = (b.width + m.left + m.right);
34851             centerX += totalWidth;
34852             centerW -= totalWidth;
34853             west.updateBox(this.safeBox(b));
34854         }
34855         if(east && east.isVisible()){
34856             var b = east.getBox();
34857             var m = east.getMargins();
34858             b.height = centerH - (m.top+m.bottom);
34859             var totalWidth = (b.width + m.left + m.right);
34860             b.x = w - totalWidth + m.left;
34861             b.y = centerY + m.top;
34862             centerW -= totalWidth;
34863             east.updateBox(this.safeBox(b));
34864         }
34865         if(center){
34866             var m = center.getMargins();
34867             var centerBox = {
34868                 x: centerX + m.left,
34869                 y: centerY + m.top,
34870                 width: centerW - (m.left+m.right),
34871                 height: centerH - (m.top+m.bottom)
34872             };
34873             //if(this.hideOnLayout){
34874                 //center.el.setStyle("display", "block");
34875             //}
34876             center.updateBox(this.safeBox(centerBox));
34877         }
34878         this.el.repaint();
34879         this.fireEvent("layout", this);
34880     },
34881
34882     // private
34883     safeBox : function(box){
34884         box.width = Math.max(0, box.width);
34885         box.height = Math.max(0, box.height);
34886         return box;
34887     },
34888
34889     /**
34890      * Adds a ContentPanel (or subclass) to this layout.
34891      * @param {String} target The target region key (north, south, east, west or center).
34892      * @param {Roo.ContentPanel} panel The panel to add
34893      * @return {Roo.ContentPanel} The added panel
34894      */
34895     add : function(target, panel){
34896          
34897         target = target.toLowerCase();
34898         return this.regions[target].add(panel);
34899     },
34900
34901     /**
34902      * Remove a ContentPanel (or subclass) to this layout.
34903      * @param {String} target The target region key (north, south, east, west or center).
34904      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34905      * @return {Roo.ContentPanel} The removed panel
34906      */
34907     remove : function(target, panel){
34908         target = target.toLowerCase();
34909         return this.regions[target].remove(panel);
34910     },
34911
34912     /**
34913      * Searches all regions for a panel with the specified id
34914      * @param {String} panelId
34915      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34916      */
34917     findPanel : function(panelId){
34918         var rs = this.regions;
34919         for(var target in rs){
34920             if(typeof rs[target] != "function"){
34921                 var p = rs[target].getPanel(panelId);
34922                 if(p){
34923                     return p;
34924                 }
34925             }
34926         }
34927         return null;
34928     },
34929
34930     /**
34931      * Searches all regions for a panel with the specified id and activates (shows) it.
34932      * @param {String/ContentPanel} panelId The panels id or the panel itself
34933      * @return {Roo.ContentPanel} The shown panel or null
34934      */
34935     showPanel : function(panelId) {
34936       var rs = this.regions;
34937       for(var target in rs){
34938          var r = rs[target];
34939          if(typeof r != "function"){
34940             if(r.hasPanel(panelId)){
34941                return r.showPanel(panelId);
34942             }
34943          }
34944       }
34945       return null;
34946    },
34947
34948    /**
34949      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34950      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34951      */
34952    /*
34953     restoreState : function(provider){
34954         if(!provider){
34955             provider = Roo.state.Manager;
34956         }
34957         var sm = new Roo.LayoutStateManager();
34958         sm.init(this, provider);
34959     },
34960 */
34961  
34962  
34963     /**
34964      * Adds a xtype elements to the layout.
34965      * <pre><code>
34966
34967 layout.addxtype({
34968        xtype : 'ContentPanel',
34969        region: 'west',
34970        items: [ .... ]
34971    }
34972 );
34973
34974 layout.addxtype({
34975         xtype : 'NestedLayoutPanel',
34976         region: 'west',
34977         layout: {
34978            center: { },
34979            west: { }   
34980         },
34981         items : [ ... list of content panels or nested layout panels.. ]
34982    }
34983 );
34984 </code></pre>
34985      * @param {Object} cfg Xtype definition of item to add.
34986      */
34987     addxtype : function(cfg)
34988     {
34989         // basically accepts a pannel...
34990         // can accept a layout region..!?!?
34991         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34992         
34993         
34994         // theory?  children can only be panels??
34995         
34996         //if (!cfg.xtype.match(/Panel$/)) {
34997         //    return false;
34998         //}
34999         var ret = false;
35000         
35001         if (typeof(cfg.region) == 'undefined') {
35002             Roo.log("Failed to add Panel, region was not set");
35003             Roo.log(cfg);
35004             return false;
35005         }
35006         var region = cfg.region;
35007         delete cfg.region;
35008         
35009           
35010         var xitems = [];
35011         if (cfg.items) {
35012             xitems = cfg.items;
35013             delete cfg.items;
35014         }
35015         var nb = false;
35016         
35017         switch(cfg.xtype) 
35018         {
35019             case 'Content':  // ContentPanel (el, cfg)
35020             case 'Scroll':  // ContentPanel (el, cfg)
35021             case 'View': 
35022                 cfg.autoCreate = true;
35023                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35024                 //} else {
35025                 //    var el = this.el.createChild();
35026                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35027                 //}
35028                 
35029                 this.add(region, ret);
35030                 break;
35031             
35032             /*
35033             case 'TreePanel': // our new panel!
35034                 cfg.el = this.el.createChild();
35035                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35036                 this.add(region, ret);
35037                 break;
35038             */
35039             
35040             case 'Nest': 
35041                 // create a new Layout (which is  a Border Layout...
35042                 
35043                 var clayout = cfg.layout;
35044                 clayout.el  = this.el.createChild();
35045                 clayout.items   = clayout.items  || [];
35046                 
35047                 delete cfg.layout;
35048                 
35049                 // replace this exitems with the clayout ones..
35050                 xitems = clayout.items;
35051                  
35052                 // force background off if it's in center...
35053                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35054                     cfg.background = false;
35055                 }
35056                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35057                 
35058                 
35059                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35060                 //console.log('adding nested layout panel '  + cfg.toSource());
35061                 this.add(region, ret);
35062                 nb = {}; /// find first...
35063                 break;
35064             
35065             case 'Grid':
35066                 
35067                 // needs grid and region
35068                 
35069                 //var el = this.getRegion(region).el.createChild();
35070                 /*
35071                  *var el = this.el.createChild();
35072                 // create the grid first...
35073                 cfg.grid.container = el;
35074                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35075                 */
35076                 
35077                 if (region == 'center' && this.active ) {
35078                     cfg.background = false;
35079                 }
35080                 
35081                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35082                 
35083                 this.add(region, ret);
35084                 /*
35085                 if (cfg.background) {
35086                     // render grid on panel activation (if panel background)
35087                     ret.on('activate', function(gp) {
35088                         if (!gp.grid.rendered) {
35089                     //        gp.grid.render(el);
35090                         }
35091                     });
35092                 } else {
35093                   //  cfg.grid.render(el);
35094                 }
35095                 */
35096                 break;
35097            
35098            
35099             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35100                 // it was the old xcomponent building that caused this before.
35101                 // espeically if border is the top element in the tree.
35102                 ret = this;
35103                 break; 
35104                 
35105                     
35106                 
35107                 
35108                 
35109             default:
35110                 /*
35111                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35112                     
35113                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35114                     this.add(region, ret);
35115                 } else {
35116                 */
35117                     Roo.log(cfg);
35118                     throw "Can not add '" + cfg.xtype + "' to Border";
35119                     return null;
35120              
35121                                 
35122              
35123         }
35124         this.beginUpdate();
35125         // add children..
35126         var region = '';
35127         var abn = {};
35128         Roo.each(xitems, function(i)  {
35129             region = nb && i.region ? i.region : false;
35130             
35131             var add = ret.addxtype(i);
35132            
35133             if (region) {
35134                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35135                 if (!i.background) {
35136                     abn[region] = nb[region] ;
35137                 }
35138             }
35139             
35140         });
35141         this.endUpdate();
35142
35143         // make the last non-background panel active..
35144         //if (nb) { Roo.log(abn); }
35145         if (nb) {
35146             
35147             for(var r in abn) {
35148                 region = this.getRegion(r);
35149                 if (region) {
35150                     // tried using nb[r], but it does not work..
35151                      
35152                     region.showPanel(abn[r]);
35153                    
35154                 }
35155             }
35156         }
35157         return ret;
35158         
35159     },
35160     
35161     
35162 // private
35163     factory : function(cfg)
35164     {
35165         
35166         var validRegions = Roo.bootstrap.layout.Border.regions;
35167
35168         var target = cfg.region;
35169         cfg.mgr = this;
35170         
35171         var r = Roo.bootstrap.layout;
35172         Roo.log(target);
35173         switch(target){
35174             case "north":
35175                 return new r.North(cfg);
35176             case "south":
35177                 return new r.South(cfg);
35178             case "east":
35179                 return new r.East(cfg);
35180             case "west":
35181                 return new r.West(cfg);
35182             case "center":
35183                 return new r.Center(cfg);
35184         }
35185         throw 'Layout region "'+target+'" not supported.';
35186     }
35187     
35188     
35189 });
35190  /*
35191  * Based on:
35192  * Ext JS Library 1.1.1
35193  * Copyright(c) 2006-2007, Ext JS, LLC.
35194  *
35195  * Originally Released Under LGPL - original licence link has changed is not relivant.
35196  *
35197  * Fork - LGPL
35198  * <script type="text/javascript">
35199  */
35200  
35201 /**
35202  * @class Roo.bootstrap.layout.Basic
35203  * @extends Roo.util.Observable
35204  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35205  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35206  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35207  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35208  * @cfg {string}   region  the region that it inhabits..
35209  * @cfg {bool}   skipConfig skip config?
35210  * 
35211
35212  */
35213 Roo.bootstrap.layout.Basic = function(config){
35214     
35215     this.mgr = config.mgr;
35216     
35217     this.position = config.region;
35218     
35219     var skipConfig = config.skipConfig;
35220     
35221     this.events = {
35222         /**
35223          * @scope Roo.BasicLayoutRegion
35224          */
35225         
35226         /**
35227          * @event beforeremove
35228          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35229          * @param {Roo.LayoutRegion} this
35230          * @param {Roo.ContentPanel} panel The panel
35231          * @param {Object} e The cancel event object
35232          */
35233         "beforeremove" : true,
35234         /**
35235          * @event invalidated
35236          * Fires when the layout for this region is changed.
35237          * @param {Roo.LayoutRegion} this
35238          */
35239         "invalidated" : true,
35240         /**
35241          * @event visibilitychange
35242          * Fires when this region is shown or hidden 
35243          * @param {Roo.LayoutRegion} this
35244          * @param {Boolean} visibility true or false
35245          */
35246         "visibilitychange" : true,
35247         /**
35248          * @event paneladded
35249          * Fires when a panel is added. 
35250          * @param {Roo.LayoutRegion} this
35251          * @param {Roo.ContentPanel} panel The panel
35252          */
35253         "paneladded" : true,
35254         /**
35255          * @event panelremoved
35256          * Fires when a panel is removed. 
35257          * @param {Roo.LayoutRegion} this
35258          * @param {Roo.ContentPanel} panel The panel
35259          */
35260         "panelremoved" : true,
35261         /**
35262          * @event beforecollapse
35263          * Fires when this region before collapse.
35264          * @param {Roo.LayoutRegion} this
35265          */
35266         "beforecollapse" : true,
35267         /**
35268          * @event collapsed
35269          * Fires when this region is collapsed.
35270          * @param {Roo.LayoutRegion} this
35271          */
35272         "collapsed" : true,
35273         /**
35274          * @event expanded
35275          * Fires when this region is expanded.
35276          * @param {Roo.LayoutRegion} this
35277          */
35278         "expanded" : true,
35279         /**
35280          * @event slideshow
35281          * Fires when this region is slid into view.
35282          * @param {Roo.LayoutRegion} this
35283          */
35284         "slideshow" : true,
35285         /**
35286          * @event slidehide
35287          * Fires when this region slides out of view. 
35288          * @param {Roo.LayoutRegion} this
35289          */
35290         "slidehide" : true,
35291         /**
35292          * @event panelactivated
35293          * Fires when a panel is activated. 
35294          * @param {Roo.LayoutRegion} this
35295          * @param {Roo.ContentPanel} panel The activated panel
35296          */
35297         "panelactivated" : true,
35298         /**
35299          * @event resized
35300          * Fires when the user resizes this region. 
35301          * @param {Roo.LayoutRegion} this
35302          * @param {Number} newSize The new size (width for east/west, height for north/south)
35303          */
35304         "resized" : true
35305     };
35306     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35307     this.panels = new Roo.util.MixedCollection();
35308     this.panels.getKey = this.getPanelId.createDelegate(this);
35309     this.box = null;
35310     this.activePanel = null;
35311     // ensure listeners are added...
35312     
35313     if (config.listeners || config.events) {
35314         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35315             listeners : config.listeners || {},
35316             events : config.events || {}
35317         });
35318     }
35319     
35320     if(skipConfig !== true){
35321         this.applyConfig(config);
35322     }
35323 };
35324
35325 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35326 {
35327     getPanelId : function(p){
35328         return p.getId();
35329     },
35330     
35331     applyConfig : function(config){
35332         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35333         this.config = config;
35334         
35335     },
35336     
35337     /**
35338      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35339      * the width, for horizontal (north, south) the height.
35340      * @param {Number} newSize The new width or height
35341      */
35342     resizeTo : function(newSize){
35343         var el = this.el ? this.el :
35344                  (this.activePanel ? this.activePanel.getEl() : null);
35345         if(el){
35346             switch(this.position){
35347                 case "east":
35348                 case "west":
35349                     el.setWidth(newSize);
35350                     this.fireEvent("resized", this, newSize);
35351                 break;
35352                 case "north":
35353                 case "south":
35354                     el.setHeight(newSize);
35355                     this.fireEvent("resized", this, newSize);
35356                 break;                
35357             }
35358         }
35359     },
35360     
35361     getBox : function(){
35362         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35363     },
35364     
35365     getMargins : function(){
35366         return this.margins;
35367     },
35368     
35369     updateBox : function(box){
35370         this.box = box;
35371         var el = this.activePanel.getEl();
35372         el.dom.style.left = box.x + "px";
35373         el.dom.style.top = box.y + "px";
35374         this.activePanel.setSize(box.width, box.height);
35375     },
35376     
35377     /**
35378      * Returns the container element for this region.
35379      * @return {Roo.Element}
35380      */
35381     getEl : function(){
35382         return this.activePanel;
35383     },
35384     
35385     /**
35386      * Returns true if this region is currently visible.
35387      * @return {Boolean}
35388      */
35389     isVisible : function(){
35390         return this.activePanel ? true : false;
35391     },
35392     
35393     setActivePanel : function(panel){
35394         panel = this.getPanel(panel);
35395         if(this.activePanel && this.activePanel != panel){
35396             this.activePanel.setActiveState(false);
35397             this.activePanel.getEl().setLeftTop(-10000,-10000);
35398         }
35399         this.activePanel = panel;
35400         panel.setActiveState(true);
35401         if(this.box){
35402             panel.setSize(this.box.width, this.box.height);
35403         }
35404         this.fireEvent("panelactivated", this, panel);
35405         this.fireEvent("invalidated");
35406     },
35407     
35408     /**
35409      * Show the specified panel.
35410      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35411      * @return {Roo.ContentPanel} The shown panel or null
35412      */
35413     showPanel : function(panel){
35414         panel = this.getPanel(panel);
35415         if(panel){
35416             this.setActivePanel(panel);
35417         }
35418         return panel;
35419     },
35420     
35421     /**
35422      * Get the active panel for this region.
35423      * @return {Roo.ContentPanel} The active panel or null
35424      */
35425     getActivePanel : function(){
35426         return this.activePanel;
35427     },
35428     
35429     /**
35430      * Add the passed ContentPanel(s)
35431      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35432      * @return {Roo.ContentPanel} The panel added (if only one was added)
35433      */
35434     add : function(panel){
35435         if(arguments.length > 1){
35436             for(var i = 0, len = arguments.length; i < len; i++) {
35437                 this.add(arguments[i]);
35438             }
35439             return null;
35440         }
35441         if(this.hasPanel(panel)){
35442             this.showPanel(panel);
35443             return panel;
35444         }
35445         var el = panel.getEl();
35446         if(el.dom.parentNode != this.mgr.el.dom){
35447             this.mgr.el.dom.appendChild(el.dom);
35448         }
35449         if(panel.setRegion){
35450             panel.setRegion(this);
35451         }
35452         this.panels.add(panel);
35453         el.setStyle("position", "absolute");
35454         if(!panel.background){
35455             this.setActivePanel(panel);
35456             if(this.config.initialSize && this.panels.getCount()==1){
35457                 this.resizeTo(this.config.initialSize);
35458             }
35459         }
35460         this.fireEvent("paneladded", this, panel);
35461         return panel;
35462     },
35463     
35464     /**
35465      * Returns true if the panel is in this region.
35466      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35467      * @return {Boolean}
35468      */
35469     hasPanel : function(panel){
35470         if(typeof panel == "object"){ // must be panel obj
35471             panel = panel.getId();
35472         }
35473         return this.getPanel(panel) ? true : false;
35474     },
35475     
35476     /**
35477      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35478      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35479      * @param {Boolean} preservePanel Overrides the config preservePanel option
35480      * @return {Roo.ContentPanel} The panel that was removed
35481      */
35482     remove : function(panel, preservePanel){
35483         panel = this.getPanel(panel);
35484         if(!panel){
35485             return null;
35486         }
35487         var e = {};
35488         this.fireEvent("beforeremove", this, panel, e);
35489         if(e.cancel === true){
35490             return null;
35491         }
35492         var panelId = panel.getId();
35493         this.panels.removeKey(panelId);
35494         return panel;
35495     },
35496     
35497     /**
35498      * Returns the panel specified or null if it's not in this region.
35499      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35500      * @return {Roo.ContentPanel}
35501      */
35502     getPanel : function(id){
35503         if(typeof id == "object"){ // must be panel obj
35504             return id;
35505         }
35506         return this.panels.get(id);
35507     },
35508     
35509     /**
35510      * Returns this regions position (north/south/east/west/center).
35511      * @return {String} 
35512      */
35513     getPosition: function(){
35514         return this.position;    
35515     }
35516 });/*
35517  * Based on:
35518  * Ext JS Library 1.1.1
35519  * Copyright(c) 2006-2007, Ext JS, LLC.
35520  *
35521  * Originally Released Under LGPL - original licence link has changed is not relivant.
35522  *
35523  * Fork - LGPL
35524  * <script type="text/javascript">
35525  */
35526  
35527 /**
35528  * @class Roo.bootstrap.layout.Region
35529  * @extends Roo.bootstrap.layout.Basic
35530  * This class represents a region in a layout manager.
35531  
35532  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35533  * @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})
35534  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35535  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35536  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35537  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35538  * @cfg {String}    title           The title for the region (overrides panel titles)
35539  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35540  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35541  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35542  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35543  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35544  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35545  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35546  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35547  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35548  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35549
35550  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35551  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35552  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35553  * @cfg {Number}    width           For East/West panels
35554  * @cfg {Number}    height          For North/South panels
35555  * @cfg {Boolean}   split           To show the splitter
35556  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35557  * 
35558  * @cfg {string}   cls             Extra CSS classes to add to region
35559  * 
35560  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35561  * @cfg {string}   region  the region that it inhabits..
35562  *
35563
35564  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35565  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35566
35567  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35568  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35569  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35570  */
35571 Roo.bootstrap.layout.Region = function(config)
35572 {
35573     this.applyConfig(config);
35574
35575     var mgr = config.mgr;
35576     var pos = config.region;
35577     config.skipConfig = true;
35578     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35579     
35580     if (mgr.el) {
35581         this.onRender(mgr.el);   
35582     }
35583      
35584     this.visible = true;
35585     this.collapsed = false;
35586     this.unrendered_panels = [];
35587 };
35588
35589 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35590
35591     position: '', // set by wrapper (eg. north/south etc..)
35592     unrendered_panels : null,  // unrendered panels.
35593     createBody : function(){
35594         /** This region's body element 
35595         * @type Roo.Element */
35596         this.bodyEl = this.el.createChild({
35597                 tag: "div",
35598                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35599         });
35600     },
35601
35602     onRender: function(ctr, pos)
35603     {
35604         var dh = Roo.DomHelper;
35605         /** This region's container element 
35606         * @type Roo.Element */
35607         this.el = dh.append(ctr.dom, {
35608                 tag: "div",
35609                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35610             }, true);
35611         /** This region's title element 
35612         * @type Roo.Element */
35613     
35614         this.titleEl = dh.append(this.el.dom,
35615             {
35616                     tag: "div",
35617                     unselectable: "on",
35618                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35619                     children:[
35620                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35621                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35622                     ]}, true);
35623         
35624         this.titleEl.enableDisplayMode();
35625         /** This region's title text element 
35626         * @type HTMLElement */
35627         this.titleTextEl = this.titleEl.dom.firstChild;
35628         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35629         /*
35630         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35631         this.closeBtn.enableDisplayMode();
35632         this.closeBtn.on("click", this.closeClicked, this);
35633         this.closeBtn.hide();
35634     */
35635         this.createBody(this.config);
35636         if(this.config.hideWhenEmpty){
35637             this.hide();
35638             this.on("paneladded", this.validateVisibility, this);
35639             this.on("panelremoved", this.validateVisibility, this);
35640         }
35641         if(this.autoScroll){
35642             this.bodyEl.setStyle("overflow", "auto");
35643         }else{
35644             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35645         }
35646         //if(c.titlebar !== false){
35647             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35648                 this.titleEl.hide();
35649             }else{
35650                 this.titleEl.show();
35651                 if(this.config.title){
35652                     this.titleTextEl.innerHTML = this.config.title;
35653                 }
35654             }
35655         //}
35656         if(this.config.collapsed){
35657             this.collapse(true);
35658         }
35659         if(this.config.hidden){
35660             this.hide();
35661         }
35662         
35663         if (this.unrendered_panels && this.unrendered_panels.length) {
35664             for (var i =0;i< this.unrendered_panels.length; i++) {
35665                 this.add(this.unrendered_panels[i]);
35666             }
35667             this.unrendered_panels = null;
35668             
35669         }
35670         
35671     },
35672     
35673     applyConfig : function(c)
35674     {
35675         /*
35676          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35677             var dh = Roo.DomHelper;
35678             if(c.titlebar !== false){
35679                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35680                 this.collapseBtn.on("click", this.collapse, this);
35681                 this.collapseBtn.enableDisplayMode();
35682                 /*
35683                 if(c.showPin === true || this.showPin){
35684                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35685                     this.stickBtn.enableDisplayMode();
35686                     this.stickBtn.on("click", this.expand, this);
35687                     this.stickBtn.hide();
35688                 }
35689                 
35690             }
35691             */
35692             /** This region's collapsed element
35693             * @type Roo.Element */
35694             /*
35695              *
35696             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35697                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35698             ]}, true);
35699             
35700             if(c.floatable !== false){
35701                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35702                this.collapsedEl.on("click", this.collapseClick, this);
35703             }
35704
35705             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35706                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35707                    id: "message", unselectable: "on", style:{"float":"left"}});
35708                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35709              }
35710             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35711             this.expandBtn.on("click", this.expand, this);
35712             
35713         }
35714         
35715         if(this.collapseBtn){
35716             this.collapseBtn.setVisible(c.collapsible == true);
35717         }
35718         
35719         this.cmargins = c.cmargins || this.cmargins ||
35720                          (this.position == "west" || this.position == "east" ?
35721                              {top: 0, left: 2, right:2, bottom: 0} :
35722                              {top: 2, left: 0, right:0, bottom: 2});
35723         */
35724         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35725         
35726         
35727         this.bottomTabs = c.tabPosition != "top";
35728         
35729         this.autoScroll = c.autoScroll || false;
35730         
35731         
35732        
35733         
35734         this.duration = c.duration || .30;
35735         this.slideDuration = c.slideDuration || .45;
35736         this.config = c;
35737        
35738     },
35739     /**
35740      * Returns true if this region is currently visible.
35741      * @return {Boolean}
35742      */
35743     isVisible : function(){
35744         return this.visible;
35745     },
35746
35747     /**
35748      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35749      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35750      */
35751     //setCollapsedTitle : function(title){
35752     //    title = title || "&#160;";
35753      //   if(this.collapsedTitleTextEl){
35754       //      this.collapsedTitleTextEl.innerHTML = title;
35755        // }
35756     //},
35757
35758     getBox : function(){
35759         var b;
35760       //  if(!this.collapsed){
35761             b = this.el.getBox(false, true);
35762        // }else{
35763           //  b = this.collapsedEl.getBox(false, true);
35764         //}
35765         return b;
35766     },
35767
35768     getMargins : function(){
35769         return this.margins;
35770         //return this.collapsed ? this.cmargins : this.margins;
35771     },
35772 /*
35773     highlight : function(){
35774         this.el.addClass("x-layout-panel-dragover");
35775     },
35776
35777     unhighlight : function(){
35778         this.el.removeClass("x-layout-panel-dragover");
35779     },
35780 */
35781     updateBox : function(box)
35782     {
35783         if (!this.bodyEl) {
35784             return; // not rendered yet..
35785         }
35786         
35787         this.box = box;
35788         if(!this.collapsed){
35789             this.el.dom.style.left = box.x + "px";
35790             this.el.dom.style.top = box.y + "px";
35791             this.updateBody(box.width, box.height);
35792         }else{
35793             this.collapsedEl.dom.style.left = box.x + "px";
35794             this.collapsedEl.dom.style.top = box.y + "px";
35795             this.collapsedEl.setSize(box.width, box.height);
35796         }
35797         if(this.tabs){
35798             this.tabs.autoSizeTabs();
35799         }
35800     },
35801
35802     updateBody : function(w, h)
35803     {
35804         if(w !== null){
35805             this.el.setWidth(w);
35806             w -= this.el.getBorderWidth("rl");
35807             if(this.config.adjustments){
35808                 w += this.config.adjustments[0];
35809             }
35810         }
35811         if(h !== null && h > 0){
35812             this.el.setHeight(h);
35813             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35814             h -= this.el.getBorderWidth("tb");
35815             if(this.config.adjustments){
35816                 h += this.config.adjustments[1];
35817             }
35818             this.bodyEl.setHeight(h);
35819             if(this.tabs){
35820                 h = this.tabs.syncHeight(h);
35821             }
35822         }
35823         if(this.panelSize){
35824             w = w !== null ? w : this.panelSize.width;
35825             h = h !== null ? h : this.panelSize.height;
35826         }
35827         if(this.activePanel){
35828             var el = this.activePanel.getEl();
35829             w = w !== null ? w : el.getWidth();
35830             h = h !== null ? h : el.getHeight();
35831             this.panelSize = {width: w, height: h};
35832             this.activePanel.setSize(w, h);
35833         }
35834         if(Roo.isIE && this.tabs){
35835             this.tabs.el.repaint();
35836         }
35837     },
35838
35839     /**
35840      * Returns the container element for this region.
35841      * @return {Roo.Element}
35842      */
35843     getEl : function(){
35844         return this.el;
35845     },
35846
35847     /**
35848      * Hides this region.
35849      */
35850     hide : function(){
35851         //if(!this.collapsed){
35852             this.el.dom.style.left = "-2000px";
35853             this.el.hide();
35854         //}else{
35855          //   this.collapsedEl.dom.style.left = "-2000px";
35856          //   this.collapsedEl.hide();
35857        // }
35858         this.visible = false;
35859         this.fireEvent("visibilitychange", this, false);
35860     },
35861
35862     /**
35863      * Shows this region if it was previously hidden.
35864      */
35865     show : function(){
35866         //if(!this.collapsed){
35867             this.el.show();
35868         //}else{
35869         //    this.collapsedEl.show();
35870        // }
35871         this.visible = true;
35872         this.fireEvent("visibilitychange", this, true);
35873     },
35874 /*
35875     closeClicked : function(){
35876         if(this.activePanel){
35877             this.remove(this.activePanel);
35878         }
35879     },
35880
35881     collapseClick : function(e){
35882         if(this.isSlid){
35883            e.stopPropagation();
35884            this.slideIn();
35885         }else{
35886            e.stopPropagation();
35887            this.slideOut();
35888         }
35889     },
35890 */
35891     /**
35892      * Collapses this region.
35893      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35894      */
35895     /*
35896     collapse : function(skipAnim, skipCheck = false){
35897         if(this.collapsed) {
35898             return;
35899         }
35900         
35901         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35902             
35903             this.collapsed = true;
35904             if(this.split){
35905                 this.split.el.hide();
35906             }
35907             if(this.config.animate && skipAnim !== true){
35908                 this.fireEvent("invalidated", this);
35909                 this.animateCollapse();
35910             }else{
35911                 this.el.setLocation(-20000,-20000);
35912                 this.el.hide();
35913                 this.collapsedEl.show();
35914                 this.fireEvent("collapsed", this);
35915                 this.fireEvent("invalidated", this);
35916             }
35917         }
35918         
35919     },
35920 */
35921     animateCollapse : function(){
35922         // overridden
35923     },
35924
35925     /**
35926      * Expands this region if it was previously collapsed.
35927      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35928      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35929      */
35930     /*
35931     expand : function(e, skipAnim){
35932         if(e) {
35933             e.stopPropagation();
35934         }
35935         if(!this.collapsed || this.el.hasActiveFx()) {
35936             return;
35937         }
35938         if(this.isSlid){
35939             this.afterSlideIn();
35940             skipAnim = true;
35941         }
35942         this.collapsed = false;
35943         if(this.config.animate && skipAnim !== true){
35944             this.animateExpand();
35945         }else{
35946             this.el.show();
35947             if(this.split){
35948                 this.split.el.show();
35949             }
35950             this.collapsedEl.setLocation(-2000,-2000);
35951             this.collapsedEl.hide();
35952             this.fireEvent("invalidated", this);
35953             this.fireEvent("expanded", this);
35954         }
35955     },
35956 */
35957     animateExpand : function(){
35958         // overridden
35959     },
35960
35961     initTabs : function()
35962     {
35963         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35964         
35965         var ts = new Roo.bootstrap.panel.Tabs({
35966                 el: this.bodyEl.dom,
35967                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35968                 disableTooltips: this.config.disableTabTips,
35969                 toolbar : this.config.toolbar
35970             });
35971         
35972         if(this.config.hideTabs){
35973             ts.stripWrap.setDisplayed(false);
35974         }
35975         this.tabs = ts;
35976         ts.resizeTabs = this.config.resizeTabs === true;
35977         ts.minTabWidth = this.config.minTabWidth || 40;
35978         ts.maxTabWidth = this.config.maxTabWidth || 250;
35979         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35980         ts.monitorResize = false;
35981         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35982         ts.bodyEl.addClass('roo-layout-tabs-body');
35983         this.panels.each(this.initPanelAsTab, this);
35984     },
35985
35986     initPanelAsTab : function(panel){
35987         var ti = this.tabs.addTab(
35988             panel.getEl().id,
35989             panel.getTitle(),
35990             null,
35991             this.config.closeOnTab && panel.isClosable(),
35992             panel.tpl
35993         );
35994         if(panel.tabTip !== undefined){
35995             ti.setTooltip(panel.tabTip);
35996         }
35997         ti.on("activate", function(){
35998               this.setActivePanel(panel);
35999         }, this);
36000         
36001         if(this.config.closeOnTab){
36002             ti.on("beforeclose", function(t, e){
36003                 e.cancel = true;
36004                 this.remove(panel);
36005             }, this);
36006         }
36007         
36008         panel.tabItem = ti;
36009         
36010         return ti;
36011     },
36012
36013     updatePanelTitle : function(panel, title)
36014     {
36015         if(this.activePanel == panel){
36016             this.updateTitle(title);
36017         }
36018         if(this.tabs){
36019             var ti = this.tabs.getTab(panel.getEl().id);
36020             ti.setText(title);
36021             if(panel.tabTip !== undefined){
36022                 ti.setTooltip(panel.tabTip);
36023             }
36024         }
36025     },
36026
36027     updateTitle : function(title){
36028         if(this.titleTextEl && !this.config.title){
36029             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36030         }
36031     },
36032
36033     setActivePanel : function(panel)
36034     {
36035         panel = this.getPanel(panel);
36036         if(this.activePanel && this.activePanel != panel){
36037             if(this.activePanel.setActiveState(false) === false){
36038                 return;
36039             }
36040         }
36041         this.activePanel = panel;
36042         panel.setActiveState(true);
36043         if(this.panelSize){
36044             panel.setSize(this.panelSize.width, this.panelSize.height);
36045         }
36046         if(this.closeBtn){
36047             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36048         }
36049         this.updateTitle(panel.getTitle());
36050         if(this.tabs){
36051             this.fireEvent("invalidated", this);
36052         }
36053         this.fireEvent("panelactivated", this, panel);
36054     },
36055
36056     /**
36057      * Shows the specified panel.
36058      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36059      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36060      */
36061     showPanel : function(panel)
36062     {
36063         panel = this.getPanel(panel);
36064         if(panel){
36065             if(this.tabs){
36066                 var tab = this.tabs.getTab(panel.getEl().id);
36067                 if(tab.isHidden()){
36068                     this.tabs.unhideTab(tab.id);
36069                 }
36070                 tab.activate();
36071             }else{
36072                 this.setActivePanel(panel);
36073             }
36074         }
36075         return panel;
36076     },
36077
36078     /**
36079      * Get the active panel for this region.
36080      * @return {Roo.ContentPanel} The active panel or null
36081      */
36082     getActivePanel : function(){
36083         return this.activePanel;
36084     },
36085
36086     validateVisibility : function(){
36087         if(this.panels.getCount() < 1){
36088             this.updateTitle("&#160;");
36089             this.closeBtn.hide();
36090             this.hide();
36091         }else{
36092             if(!this.isVisible()){
36093                 this.show();
36094             }
36095         }
36096     },
36097
36098     /**
36099      * Adds the passed ContentPanel(s) to this region.
36100      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36101      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36102      */
36103     add : function(panel)
36104     {
36105         if(arguments.length > 1){
36106             for(var i = 0, len = arguments.length; i < len; i++) {
36107                 this.add(arguments[i]);
36108             }
36109             return null;
36110         }
36111         
36112         // if we have not been rendered yet, then we can not really do much of this..
36113         if (!this.bodyEl) {
36114             this.unrendered_panels.push(panel);
36115             return panel;
36116         }
36117         
36118         
36119         
36120         
36121         if(this.hasPanel(panel)){
36122             this.showPanel(panel);
36123             return panel;
36124         }
36125         panel.setRegion(this);
36126         this.panels.add(panel);
36127        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36128             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36129             // and hide them... ???
36130             this.bodyEl.dom.appendChild(panel.getEl().dom);
36131             if(panel.background !== true){
36132                 this.setActivePanel(panel);
36133             }
36134             this.fireEvent("paneladded", this, panel);
36135             return panel;
36136         }
36137         */
36138         if(!this.tabs){
36139             this.initTabs();
36140         }else{
36141             this.initPanelAsTab(panel);
36142         }
36143         
36144         
36145         if(panel.background !== true){
36146             this.tabs.activate(panel.getEl().id);
36147         }
36148         this.fireEvent("paneladded", this, panel);
36149         return panel;
36150     },
36151
36152     /**
36153      * Hides the tab for the specified panel.
36154      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36155      */
36156     hidePanel : function(panel){
36157         if(this.tabs && (panel = this.getPanel(panel))){
36158             this.tabs.hideTab(panel.getEl().id);
36159         }
36160     },
36161
36162     /**
36163      * Unhides the tab for a previously hidden panel.
36164      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36165      */
36166     unhidePanel : function(panel){
36167         if(this.tabs && (panel = this.getPanel(panel))){
36168             this.tabs.unhideTab(panel.getEl().id);
36169         }
36170     },
36171
36172     clearPanels : function(){
36173         while(this.panels.getCount() > 0){
36174              this.remove(this.panels.first());
36175         }
36176     },
36177
36178     /**
36179      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36180      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36181      * @param {Boolean} preservePanel Overrides the config preservePanel option
36182      * @return {Roo.ContentPanel} The panel that was removed
36183      */
36184     remove : function(panel, preservePanel)
36185     {
36186         panel = this.getPanel(panel);
36187         if(!panel){
36188             return null;
36189         }
36190         var e = {};
36191         this.fireEvent("beforeremove", this, panel, e);
36192         if(e.cancel === true){
36193             return null;
36194         }
36195         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36196         var panelId = panel.getId();
36197         this.panels.removeKey(panelId);
36198         if(preservePanel){
36199             document.body.appendChild(panel.getEl().dom);
36200         }
36201         if(this.tabs){
36202             this.tabs.removeTab(panel.getEl().id);
36203         }else if (!preservePanel){
36204             this.bodyEl.dom.removeChild(panel.getEl().dom);
36205         }
36206         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36207             var p = this.panels.first();
36208             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36209             tempEl.appendChild(p.getEl().dom);
36210             this.bodyEl.update("");
36211             this.bodyEl.dom.appendChild(p.getEl().dom);
36212             tempEl = null;
36213             this.updateTitle(p.getTitle());
36214             this.tabs = null;
36215             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36216             this.setActivePanel(p);
36217         }
36218         panel.setRegion(null);
36219         if(this.activePanel == panel){
36220             this.activePanel = null;
36221         }
36222         if(this.config.autoDestroy !== false && preservePanel !== true){
36223             try{panel.destroy();}catch(e){}
36224         }
36225         this.fireEvent("panelremoved", this, panel);
36226         return panel;
36227     },
36228
36229     /**
36230      * Returns the TabPanel component used by this region
36231      * @return {Roo.TabPanel}
36232      */
36233     getTabs : function(){
36234         return this.tabs;
36235     },
36236
36237     createTool : function(parentEl, className){
36238         var btn = Roo.DomHelper.append(parentEl, {
36239             tag: "div",
36240             cls: "x-layout-tools-button",
36241             children: [ {
36242                 tag: "div",
36243                 cls: "roo-layout-tools-button-inner " + className,
36244                 html: "&#160;"
36245             }]
36246         }, true);
36247         btn.addClassOnOver("roo-layout-tools-button-over");
36248         return btn;
36249     }
36250 });/*
36251  * Based on:
36252  * Ext JS Library 1.1.1
36253  * Copyright(c) 2006-2007, Ext JS, LLC.
36254  *
36255  * Originally Released Under LGPL - original licence link has changed is not relivant.
36256  *
36257  * Fork - LGPL
36258  * <script type="text/javascript">
36259  */
36260  
36261
36262
36263 /**
36264  * @class Roo.SplitLayoutRegion
36265  * @extends Roo.LayoutRegion
36266  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36267  */
36268 Roo.bootstrap.layout.Split = function(config){
36269     this.cursor = config.cursor;
36270     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36271 };
36272
36273 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36274 {
36275     splitTip : "Drag to resize.",
36276     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36277     useSplitTips : false,
36278
36279     applyConfig : function(config){
36280         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36281     },
36282     
36283     onRender : function(ctr,pos) {
36284         
36285         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36286         if(!this.config.split){
36287             return;
36288         }
36289         if(!this.split){
36290             
36291             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36292                             tag: "div",
36293                             id: this.el.id + "-split",
36294                             cls: "roo-layout-split roo-layout-split-"+this.position,
36295                             html: "&#160;"
36296             });
36297             /** The SplitBar for this region 
36298             * @type Roo.SplitBar */
36299             // does not exist yet...
36300             Roo.log([this.position, this.orientation]);
36301             
36302             this.split = new Roo.bootstrap.SplitBar({
36303                 dragElement : splitEl,
36304                 resizingElement: this.el,
36305                 orientation : this.orientation
36306             });
36307             
36308             this.split.on("moved", this.onSplitMove, this);
36309             this.split.useShim = this.config.useShim === true;
36310             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36311             if(this.useSplitTips){
36312                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36313             }
36314             //if(config.collapsible){
36315             //    this.split.el.on("dblclick", this.collapse,  this);
36316             //}
36317         }
36318         if(typeof this.config.minSize != "undefined"){
36319             this.split.minSize = this.config.minSize;
36320         }
36321         if(typeof this.config.maxSize != "undefined"){
36322             this.split.maxSize = this.config.maxSize;
36323         }
36324         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36325             this.hideSplitter();
36326         }
36327         
36328     },
36329
36330     getHMaxSize : function(){
36331          var cmax = this.config.maxSize || 10000;
36332          var center = this.mgr.getRegion("center");
36333          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36334     },
36335
36336     getVMaxSize : function(){
36337          var cmax = this.config.maxSize || 10000;
36338          var center = this.mgr.getRegion("center");
36339          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36340     },
36341
36342     onSplitMove : function(split, newSize){
36343         this.fireEvent("resized", this, newSize);
36344     },
36345     
36346     /** 
36347      * Returns the {@link Roo.SplitBar} for this region.
36348      * @return {Roo.SplitBar}
36349      */
36350     getSplitBar : function(){
36351         return this.split;
36352     },
36353     
36354     hide : function(){
36355         this.hideSplitter();
36356         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36357     },
36358
36359     hideSplitter : function(){
36360         if(this.split){
36361             this.split.el.setLocation(-2000,-2000);
36362             this.split.el.hide();
36363         }
36364     },
36365
36366     show : function(){
36367         if(this.split){
36368             this.split.el.show();
36369         }
36370         Roo.bootstrap.layout.Split.superclass.show.call(this);
36371     },
36372     
36373     beforeSlide: function(){
36374         if(Roo.isGecko){// firefox overflow auto bug workaround
36375             this.bodyEl.clip();
36376             if(this.tabs) {
36377                 this.tabs.bodyEl.clip();
36378             }
36379             if(this.activePanel){
36380                 this.activePanel.getEl().clip();
36381                 
36382                 if(this.activePanel.beforeSlide){
36383                     this.activePanel.beforeSlide();
36384                 }
36385             }
36386         }
36387     },
36388     
36389     afterSlide : function(){
36390         if(Roo.isGecko){// firefox overflow auto bug workaround
36391             this.bodyEl.unclip();
36392             if(this.tabs) {
36393                 this.tabs.bodyEl.unclip();
36394             }
36395             if(this.activePanel){
36396                 this.activePanel.getEl().unclip();
36397                 if(this.activePanel.afterSlide){
36398                     this.activePanel.afterSlide();
36399                 }
36400             }
36401         }
36402     },
36403
36404     initAutoHide : function(){
36405         if(this.autoHide !== false){
36406             if(!this.autoHideHd){
36407                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36408                 this.autoHideHd = {
36409                     "mouseout": function(e){
36410                         if(!e.within(this.el, true)){
36411                             st.delay(500);
36412                         }
36413                     },
36414                     "mouseover" : function(e){
36415                         st.cancel();
36416                     },
36417                     scope : this
36418                 };
36419             }
36420             this.el.on(this.autoHideHd);
36421         }
36422     },
36423
36424     clearAutoHide : function(){
36425         if(this.autoHide !== false){
36426             this.el.un("mouseout", this.autoHideHd.mouseout);
36427             this.el.un("mouseover", this.autoHideHd.mouseover);
36428         }
36429     },
36430
36431     clearMonitor : function(){
36432         Roo.get(document).un("click", this.slideInIf, this);
36433     },
36434
36435     // these names are backwards but not changed for compat
36436     slideOut : function(){
36437         if(this.isSlid || this.el.hasActiveFx()){
36438             return;
36439         }
36440         this.isSlid = true;
36441         if(this.collapseBtn){
36442             this.collapseBtn.hide();
36443         }
36444         this.closeBtnState = this.closeBtn.getStyle('display');
36445         this.closeBtn.hide();
36446         if(this.stickBtn){
36447             this.stickBtn.show();
36448         }
36449         this.el.show();
36450         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36451         this.beforeSlide();
36452         this.el.setStyle("z-index", 10001);
36453         this.el.slideIn(this.getSlideAnchor(), {
36454             callback: function(){
36455                 this.afterSlide();
36456                 this.initAutoHide();
36457                 Roo.get(document).on("click", this.slideInIf, this);
36458                 this.fireEvent("slideshow", this);
36459             },
36460             scope: this,
36461             block: true
36462         });
36463     },
36464
36465     afterSlideIn : function(){
36466         this.clearAutoHide();
36467         this.isSlid = false;
36468         this.clearMonitor();
36469         this.el.setStyle("z-index", "");
36470         if(this.collapseBtn){
36471             this.collapseBtn.show();
36472         }
36473         this.closeBtn.setStyle('display', this.closeBtnState);
36474         if(this.stickBtn){
36475             this.stickBtn.hide();
36476         }
36477         this.fireEvent("slidehide", this);
36478     },
36479
36480     slideIn : function(cb){
36481         if(!this.isSlid || this.el.hasActiveFx()){
36482             Roo.callback(cb);
36483             return;
36484         }
36485         this.isSlid = false;
36486         this.beforeSlide();
36487         this.el.slideOut(this.getSlideAnchor(), {
36488             callback: function(){
36489                 this.el.setLeftTop(-10000, -10000);
36490                 this.afterSlide();
36491                 this.afterSlideIn();
36492                 Roo.callback(cb);
36493             },
36494             scope: this,
36495             block: true
36496         });
36497     },
36498     
36499     slideInIf : function(e){
36500         if(!e.within(this.el)){
36501             this.slideIn();
36502         }
36503     },
36504
36505     animateCollapse : function(){
36506         this.beforeSlide();
36507         this.el.setStyle("z-index", 20000);
36508         var anchor = this.getSlideAnchor();
36509         this.el.slideOut(anchor, {
36510             callback : function(){
36511                 this.el.setStyle("z-index", "");
36512                 this.collapsedEl.slideIn(anchor, {duration:.3});
36513                 this.afterSlide();
36514                 this.el.setLocation(-10000,-10000);
36515                 this.el.hide();
36516                 this.fireEvent("collapsed", this);
36517             },
36518             scope: this,
36519             block: true
36520         });
36521     },
36522
36523     animateExpand : function(){
36524         this.beforeSlide();
36525         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36526         this.el.setStyle("z-index", 20000);
36527         this.collapsedEl.hide({
36528             duration:.1
36529         });
36530         this.el.slideIn(this.getSlideAnchor(), {
36531             callback : function(){
36532                 this.el.setStyle("z-index", "");
36533                 this.afterSlide();
36534                 if(this.split){
36535                     this.split.el.show();
36536                 }
36537                 this.fireEvent("invalidated", this);
36538                 this.fireEvent("expanded", this);
36539             },
36540             scope: this,
36541             block: true
36542         });
36543     },
36544
36545     anchors : {
36546         "west" : "left",
36547         "east" : "right",
36548         "north" : "top",
36549         "south" : "bottom"
36550     },
36551
36552     sanchors : {
36553         "west" : "l",
36554         "east" : "r",
36555         "north" : "t",
36556         "south" : "b"
36557     },
36558
36559     canchors : {
36560         "west" : "tl-tr",
36561         "east" : "tr-tl",
36562         "north" : "tl-bl",
36563         "south" : "bl-tl"
36564     },
36565
36566     getAnchor : function(){
36567         return this.anchors[this.position];
36568     },
36569
36570     getCollapseAnchor : function(){
36571         return this.canchors[this.position];
36572     },
36573
36574     getSlideAnchor : function(){
36575         return this.sanchors[this.position];
36576     },
36577
36578     getAlignAdj : function(){
36579         var cm = this.cmargins;
36580         switch(this.position){
36581             case "west":
36582                 return [0, 0];
36583             break;
36584             case "east":
36585                 return [0, 0];
36586             break;
36587             case "north":
36588                 return [0, 0];
36589             break;
36590             case "south":
36591                 return [0, 0];
36592             break;
36593         }
36594     },
36595
36596     getExpandAdj : function(){
36597         var c = this.collapsedEl, cm = this.cmargins;
36598         switch(this.position){
36599             case "west":
36600                 return [-(cm.right+c.getWidth()+cm.left), 0];
36601             break;
36602             case "east":
36603                 return [cm.right+c.getWidth()+cm.left, 0];
36604             break;
36605             case "north":
36606                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36607             break;
36608             case "south":
36609                 return [0, cm.top+cm.bottom+c.getHeight()];
36610             break;
36611         }
36612     }
36613 });/*
36614  * Based on:
36615  * Ext JS Library 1.1.1
36616  * Copyright(c) 2006-2007, Ext JS, LLC.
36617  *
36618  * Originally Released Under LGPL - original licence link has changed is not relivant.
36619  *
36620  * Fork - LGPL
36621  * <script type="text/javascript">
36622  */
36623 /*
36624  * These classes are private internal classes
36625  */
36626 Roo.bootstrap.layout.Center = function(config){
36627     config.region = "center";
36628     Roo.bootstrap.layout.Region.call(this, config);
36629     this.visible = true;
36630     this.minWidth = config.minWidth || 20;
36631     this.minHeight = config.minHeight || 20;
36632 };
36633
36634 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36635     hide : function(){
36636         // center panel can't be hidden
36637     },
36638     
36639     show : function(){
36640         // center panel can't be hidden
36641     },
36642     
36643     getMinWidth: function(){
36644         return this.minWidth;
36645     },
36646     
36647     getMinHeight: function(){
36648         return this.minHeight;
36649     }
36650 });
36651
36652
36653
36654
36655  
36656
36657
36658
36659
36660
36661 Roo.bootstrap.layout.North = function(config)
36662 {
36663     config.region = 'north';
36664     config.cursor = 'n-resize';
36665     
36666     Roo.bootstrap.layout.Split.call(this, config);
36667     
36668     
36669     if(this.split){
36670         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36671         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36672         this.split.el.addClass("roo-layout-split-v");
36673     }
36674     var size = config.initialSize || config.height;
36675     if(typeof size != "undefined"){
36676         this.el.setHeight(size);
36677     }
36678 };
36679 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36680 {
36681     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36682     
36683     
36684     
36685     getBox : function(){
36686         if(this.collapsed){
36687             return this.collapsedEl.getBox();
36688         }
36689         var box = this.el.getBox();
36690         if(this.split){
36691             box.height += this.split.el.getHeight();
36692         }
36693         return box;
36694     },
36695     
36696     updateBox : function(box){
36697         if(this.split && !this.collapsed){
36698             box.height -= this.split.el.getHeight();
36699             this.split.el.setLeft(box.x);
36700             this.split.el.setTop(box.y+box.height);
36701             this.split.el.setWidth(box.width);
36702         }
36703         if(this.collapsed){
36704             this.updateBody(box.width, null);
36705         }
36706         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36707     }
36708 });
36709
36710
36711
36712
36713
36714 Roo.bootstrap.layout.South = function(config){
36715     config.region = 'south';
36716     config.cursor = 's-resize';
36717     Roo.bootstrap.layout.Split.call(this, config);
36718     if(this.split){
36719         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36720         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36721         this.split.el.addClass("roo-layout-split-v");
36722     }
36723     var size = config.initialSize || config.height;
36724     if(typeof size != "undefined"){
36725         this.el.setHeight(size);
36726     }
36727 };
36728
36729 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36730     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36731     getBox : function(){
36732         if(this.collapsed){
36733             return this.collapsedEl.getBox();
36734         }
36735         var box = this.el.getBox();
36736         if(this.split){
36737             var sh = this.split.el.getHeight();
36738             box.height += sh;
36739             box.y -= sh;
36740         }
36741         return box;
36742     },
36743     
36744     updateBox : function(box){
36745         if(this.split && !this.collapsed){
36746             var sh = this.split.el.getHeight();
36747             box.height -= sh;
36748             box.y += sh;
36749             this.split.el.setLeft(box.x);
36750             this.split.el.setTop(box.y-sh);
36751             this.split.el.setWidth(box.width);
36752         }
36753         if(this.collapsed){
36754             this.updateBody(box.width, null);
36755         }
36756         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36757     }
36758 });
36759
36760 Roo.bootstrap.layout.East = function(config){
36761     config.region = "east";
36762     config.cursor = "e-resize";
36763     Roo.bootstrap.layout.Split.call(this, config);
36764     if(this.split){
36765         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36766         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36767         this.split.el.addClass("roo-layout-split-h");
36768     }
36769     var size = config.initialSize || config.width;
36770     if(typeof size != "undefined"){
36771         this.el.setWidth(size);
36772     }
36773 };
36774 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36775     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36776     getBox : function(){
36777         if(this.collapsed){
36778             return this.collapsedEl.getBox();
36779         }
36780         var box = this.el.getBox();
36781         if(this.split){
36782             var sw = this.split.el.getWidth();
36783             box.width += sw;
36784             box.x -= sw;
36785         }
36786         return box;
36787     },
36788
36789     updateBox : function(box){
36790         if(this.split && !this.collapsed){
36791             var sw = this.split.el.getWidth();
36792             box.width -= sw;
36793             this.split.el.setLeft(box.x);
36794             this.split.el.setTop(box.y);
36795             this.split.el.setHeight(box.height);
36796             box.x += sw;
36797         }
36798         if(this.collapsed){
36799             this.updateBody(null, box.height);
36800         }
36801         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36802     }
36803 });
36804
36805 Roo.bootstrap.layout.West = function(config){
36806     config.region = "west";
36807     config.cursor = "w-resize";
36808     
36809     Roo.bootstrap.layout.Split.call(this, config);
36810     if(this.split){
36811         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36812         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36813         this.split.el.addClass("roo-layout-split-h");
36814     }
36815     
36816 };
36817 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36818     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36819     
36820     onRender: function(ctr, pos)
36821     {
36822         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36823         var size = this.config.initialSize || this.config.width;
36824         if(typeof size != "undefined"){
36825             this.el.setWidth(size);
36826         }
36827     },
36828     
36829     getBox : function(){
36830         if(this.collapsed){
36831             return this.collapsedEl.getBox();
36832         }
36833         var box = this.el.getBox();
36834         if(this.split){
36835             box.width += this.split.el.getWidth();
36836         }
36837         return box;
36838     },
36839     
36840     updateBox : function(box){
36841         if(this.split && !this.collapsed){
36842             var sw = this.split.el.getWidth();
36843             box.width -= sw;
36844             this.split.el.setLeft(box.x+box.width);
36845             this.split.el.setTop(box.y);
36846             this.split.el.setHeight(box.height);
36847         }
36848         if(this.collapsed){
36849             this.updateBody(null, box.height);
36850         }
36851         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36852     }
36853 });
36854 Roo.namespace("Roo.bootstrap.panel");/*
36855  * Based on:
36856  * Ext JS Library 1.1.1
36857  * Copyright(c) 2006-2007, Ext JS, LLC.
36858  *
36859  * Originally Released Under LGPL - original licence link has changed is not relivant.
36860  *
36861  * Fork - LGPL
36862  * <script type="text/javascript">
36863  */
36864 /**
36865  * @class Roo.ContentPanel
36866  * @extends Roo.util.Observable
36867  * A basic ContentPanel element.
36868  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36869  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36870  * @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
36871  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36872  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36873  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36874  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36875  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36876  * @cfg {String} title          The title for this panel
36877  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36878  * @cfg {String} url            Calls {@link #setUrl} with this value
36879  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36880  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36881  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36882  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36883  * @cfg {Boolean} badges render the badges
36884
36885  * @constructor
36886  * Create a new ContentPanel.
36887  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36888  * @param {String/Object} config A string to set only the title or a config object
36889  * @param {String} content (optional) Set the HTML content for this panel
36890  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36891  */
36892 Roo.bootstrap.panel.Content = function( config){
36893     
36894     this.tpl = config.tpl || false;
36895     
36896     var el = config.el;
36897     var content = config.content;
36898
36899     if(config.autoCreate){ // xtype is available if this is called from factory
36900         el = Roo.id();
36901     }
36902     this.el = Roo.get(el);
36903     if(!this.el && config && config.autoCreate){
36904         if(typeof config.autoCreate == "object"){
36905             if(!config.autoCreate.id){
36906                 config.autoCreate.id = config.id||el;
36907             }
36908             this.el = Roo.DomHelper.append(document.body,
36909                         config.autoCreate, true);
36910         }else{
36911             var elcfg =  {   tag: "div",
36912                             cls: "roo-layout-inactive-content",
36913                             id: config.id||el
36914                             };
36915             if (config.html) {
36916                 elcfg.html = config.html;
36917                 
36918             }
36919                         
36920             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36921         }
36922     } 
36923     this.closable = false;
36924     this.loaded = false;
36925     this.active = false;
36926    
36927       
36928     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36929         
36930         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36931         
36932         this.wrapEl = this.el; //this.el.wrap();
36933         var ti = [];
36934         if (config.toolbar.items) {
36935             ti = config.toolbar.items ;
36936             delete config.toolbar.items ;
36937         }
36938         
36939         var nitems = [];
36940         this.toolbar.render(this.wrapEl, 'before');
36941         for(var i =0;i < ti.length;i++) {
36942           //  Roo.log(['add child', items[i]]);
36943             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36944         }
36945         this.toolbar.items = nitems;
36946         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36947         delete config.toolbar;
36948         
36949     }
36950     /*
36951     // xtype created footer. - not sure if will work as we normally have to render first..
36952     if (this.footer && !this.footer.el && this.footer.xtype) {
36953         if (!this.wrapEl) {
36954             this.wrapEl = this.el.wrap();
36955         }
36956     
36957         this.footer.container = this.wrapEl.createChild();
36958          
36959         this.footer = Roo.factory(this.footer, Roo);
36960         
36961     }
36962     */
36963     
36964      if(typeof config == "string"){
36965         this.title = config;
36966     }else{
36967         Roo.apply(this, config);
36968     }
36969     
36970     if(this.resizeEl){
36971         this.resizeEl = Roo.get(this.resizeEl, true);
36972     }else{
36973         this.resizeEl = this.el;
36974     }
36975     // handle view.xtype
36976     
36977  
36978     
36979     
36980     this.addEvents({
36981         /**
36982          * @event activate
36983          * Fires when this panel is activated. 
36984          * @param {Roo.ContentPanel} this
36985          */
36986         "activate" : true,
36987         /**
36988          * @event deactivate
36989          * Fires when this panel is activated. 
36990          * @param {Roo.ContentPanel} this
36991          */
36992         "deactivate" : true,
36993
36994         /**
36995          * @event resize
36996          * Fires when this panel is resized if fitToFrame is true.
36997          * @param {Roo.ContentPanel} this
36998          * @param {Number} width The width after any component adjustments
36999          * @param {Number} height The height after any component adjustments
37000          */
37001         "resize" : true,
37002         
37003          /**
37004          * @event render
37005          * Fires when this tab is created
37006          * @param {Roo.ContentPanel} this
37007          */
37008         "render" : true
37009         
37010         
37011         
37012     });
37013     
37014
37015     
37016     
37017     if(this.autoScroll){
37018         this.resizeEl.setStyle("overflow", "auto");
37019     } else {
37020         // fix randome scrolling
37021         //this.el.on('scroll', function() {
37022         //    Roo.log('fix random scolling');
37023         //    this.scrollTo('top',0); 
37024         //});
37025     }
37026     content = content || this.content;
37027     if(content){
37028         this.setContent(content);
37029     }
37030     if(config && config.url){
37031         this.setUrl(this.url, this.params, this.loadOnce);
37032     }
37033     
37034     
37035     
37036     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37037     
37038     if (this.view && typeof(this.view.xtype) != 'undefined') {
37039         this.view.el = this.el.appendChild(document.createElement("div"));
37040         this.view = Roo.factory(this.view); 
37041         this.view.render  &&  this.view.render(false, '');  
37042     }
37043     
37044     
37045     this.fireEvent('render', this);
37046 };
37047
37048 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37049     
37050     tabTip : '',
37051     
37052     setRegion : function(region){
37053         this.region = region;
37054         this.setActiveClass(region && !this.background);
37055     },
37056     
37057     
37058     setActiveClass: function(state)
37059     {
37060         if(state){
37061            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37062            this.el.setStyle('position','relative');
37063         }else{
37064            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37065            this.el.setStyle('position', 'absolute');
37066         } 
37067     },
37068     
37069     /**
37070      * Returns the toolbar for this Panel if one was configured. 
37071      * @return {Roo.Toolbar} 
37072      */
37073     getToolbar : function(){
37074         return this.toolbar;
37075     },
37076     
37077     setActiveState : function(active)
37078     {
37079         this.active = active;
37080         this.setActiveClass(active);
37081         if(!active){
37082             if(this.fireEvent("deactivate", this) === false){
37083                 return false;
37084             }
37085             return true;
37086         }
37087         this.fireEvent("activate", this);
37088         return true;
37089     },
37090     /**
37091      * Updates this panel's element
37092      * @param {String} content The new content
37093      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37094     */
37095     setContent : function(content, loadScripts){
37096         this.el.update(content, loadScripts);
37097     },
37098
37099     ignoreResize : function(w, h){
37100         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37101             return true;
37102         }else{
37103             this.lastSize = {width: w, height: h};
37104             return false;
37105         }
37106     },
37107     /**
37108      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37109      * @return {Roo.UpdateManager} The UpdateManager
37110      */
37111     getUpdateManager : function(){
37112         return this.el.getUpdateManager();
37113     },
37114      /**
37115      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37116      * @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:
37117 <pre><code>
37118 panel.load({
37119     url: "your-url.php",
37120     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37121     callback: yourFunction,
37122     scope: yourObject, //(optional scope)
37123     discardUrl: false,
37124     nocache: false,
37125     text: "Loading...",
37126     timeout: 30,
37127     scripts: false
37128 });
37129 </code></pre>
37130      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37131      * 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.
37132      * @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}
37133      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37134      * @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.
37135      * @return {Roo.ContentPanel} this
37136      */
37137     load : function(){
37138         var um = this.el.getUpdateManager();
37139         um.update.apply(um, arguments);
37140         return this;
37141     },
37142
37143
37144     /**
37145      * 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.
37146      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37147      * @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)
37148      * @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)
37149      * @return {Roo.UpdateManager} The UpdateManager
37150      */
37151     setUrl : function(url, params, loadOnce){
37152         if(this.refreshDelegate){
37153             this.removeListener("activate", this.refreshDelegate);
37154         }
37155         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37156         this.on("activate", this.refreshDelegate);
37157         return this.el.getUpdateManager();
37158     },
37159     
37160     _handleRefresh : function(url, params, loadOnce){
37161         if(!loadOnce || !this.loaded){
37162             var updater = this.el.getUpdateManager();
37163             updater.update(url, params, this._setLoaded.createDelegate(this));
37164         }
37165     },
37166     
37167     _setLoaded : function(){
37168         this.loaded = true;
37169     }, 
37170     
37171     /**
37172      * Returns this panel's id
37173      * @return {String} 
37174      */
37175     getId : function(){
37176         return this.el.id;
37177     },
37178     
37179     /** 
37180      * Returns this panel's element - used by regiosn to add.
37181      * @return {Roo.Element} 
37182      */
37183     getEl : function(){
37184         return this.wrapEl || this.el;
37185     },
37186     
37187    
37188     
37189     adjustForComponents : function(width, height)
37190     {
37191         //Roo.log('adjustForComponents ');
37192         if(this.resizeEl != this.el){
37193             width -= this.el.getFrameWidth('lr');
37194             height -= this.el.getFrameWidth('tb');
37195         }
37196         if(this.toolbar){
37197             var te = this.toolbar.getEl();
37198             te.setWidth(width);
37199             height -= te.getHeight();
37200         }
37201         if(this.footer){
37202             var te = this.footer.getEl();
37203             te.setWidth(width);
37204             height -= te.getHeight();
37205         }
37206         
37207         
37208         if(this.adjustments){
37209             width += this.adjustments[0];
37210             height += this.adjustments[1];
37211         }
37212         return {"width": width, "height": height};
37213     },
37214     
37215     setSize : function(width, height){
37216         if(this.fitToFrame && !this.ignoreResize(width, height)){
37217             if(this.fitContainer && this.resizeEl != this.el){
37218                 this.el.setSize(width, height);
37219             }
37220             var size = this.adjustForComponents(width, height);
37221             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37222             this.fireEvent('resize', this, size.width, size.height);
37223         }
37224     },
37225     
37226     /**
37227      * Returns this panel's title
37228      * @return {String} 
37229      */
37230     getTitle : function(){
37231         
37232         if (typeof(this.title) != 'object') {
37233             return this.title;
37234         }
37235         
37236         var t = '';
37237         for (var k in this.title) {
37238             if (!this.title.hasOwnProperty(k)) {
37239                 continue;
37240             }
37241             
37242             if (k.indexOf('-') >= 0) {
37243                 var s = k.split('-');
37244                 for (var i = 0; i<s.length; i++) {
37245                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37246                 }
37247             } else {
37248                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37249             }
37250         }
37251         return t;
37252     },
37253     
37254     /**
37255      * Set this panel's title
37256      * @param {String} title
37257      */
37258     setTitle : function(title){
37259         this.title = title;
37260         if(this.region){
37261             this.region.updatePanelTitle(this, title);
37262         }
37263     },
37264     
37265     /**
37266      * Returns true is this panel was configured to be closable
37267      * @return {Boolean} 
37268      */
37269     isClosable : function(){
37270         return this.closable;
37271     },
37272     
37273     beforeSlide : function(){
37274         this.el.clip();
37275         this.resizeEl.clip();
37276     },
37277     
37278     afterSlide : function(){
37279         this.el.unclip();
37280         this.resizeEl.unclip();
37281     },
37282     
37283     /**
37284      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37285      *   Will fail silently if the {@link #setUrl} method has not been called.
37286      *   This does not activate the panel, just updates its content.
37287      */
37288     refresh : function(){
37289         if(this.refreshDelegate){
37290            this.loaded = false;
37291            this.refreshDelegate();
37292         }
37293     },
37294     
37295     /**
37296      * Destroys this panel
37297      */
37298     destroy : function(){
37299         this.el.removeAllListeners();
37300         var tempEl = document.createElement("span");
37301         tempEl.appendChild(this.el.dom);
37302         tempEl.innerHTML = "";
37303         this.el.remove();
37304         this.el = null;
37305     },
37306     
37307     /**
37308      * form - if the content panel contains a form - this is a reference to it.
37309      * @type {Roo.form.Form}
37310      */
37311     form : false,
37312     /**
37313      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37314      *    This contains a reference to it.
37315      * @type {Roo.View}
37316      */
37317     view : false,
37318     
37319       /**
37320      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37321      * <pre><code>
37322
37323 layout.addxtype({
37324        xtype : 'Form',
37325        items: [ .... ]
37326    }
37327 );
37328
37329 </code></pre>
37330      * @param {Object} cfg Xtype definition of item to add.
37331      */
37332     
37333     
37334     getChildContainer: function () {
37335         return this.getEl();
37336     }
37337     
37338     
37339     /*
37340         var  ret = new Roo.factory(cfg);
37341         return ret;
37342         
37343         
37344         // add form..
37345         if (cfg.xtype.match(/^Form$/)) {
37346             
37347             var el;
37348             //if (this.footer) {
37349             //    el = this.footer.container.insertSibling(false, 'before');
37350             //} else {
37351                 el = this.el.createChild();
37352             //}
37353
37354             this.form = new  Roo.form.Form(cfg);
37355             
37356             
37357             if ( this.form.allItems.length) {
37358                 this.form.render(el.dom);
37359             }
37360             return this.form;
37361         }
37362         // should only have one of theses..
37363         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37364             // views.. should not be just added - used named prop 'view''
37365             
37366             cfg.el = this.el.appendChild(document.createElement("div"));
37367             // factory?
37368             
37369             var ret = new Roo.factory(cfg);
37370              
37371              ret.render && ret.render(false, ''); // render blank..
37372             this.view = ret;
37373             return ret;
37374         }
37375         return false;
37376     }
37377     \*/
37378 });
37379  
37380 /**
37381  * @class Roo.bootstrap.panel.Grid
37382  * @extends Roo.bootstrap.panel.Content
37383  * @constructor
37384  * Create a new GridPanel.
37385  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37386  * @param {Object} config A the config object
37387   
37388  */
37389
37390
37391
37392 Roo.bootstrap.panel.Grid = function(config)
37393 {
37394     
37395       
37396     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37397         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37398
37399     config.el = this.wrapper;
37400     //this.el = this.wrapper;
37401     
37402       if (config.container) {
37403         // ctor'ed from a Border/panel.grid
37404         
37405         
37406         this.wrapper.setStyle("overflow", "hidden");
37407         this.wrapper.addClass('roo-grid-container');
37408
37409     }
37410     
37411     
37412     if(config.toolbar){
37413         var tool_el = this.wrapper.createChild();    
37414         this.toolbar = Roo.factory(config.toolbar);
37415         var ti = [];
37416         if (config.toolbar.items) {
37417             ti = config.toolbar.items ;
37418             delete config.toolbar.items ;
37419         }
37420         
37421         var nitems = [];
37422         this.toolbar.render(tool_el);
37423         for(var i =0;i < ti.length;i++) {
37424           //  Roo.log(['add child', items[i]]);
37425             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37426         }
37427         this.toolbar.items = nitems;
37428         
37429         delete config.toolbar;
37430     }
37431     
37432     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37433     config.grid.scrollBody = true;;
37434     config.grid.monitorWindowResize = false; // turn off autosizing
37435     config.grid.autoHeight = false;
37436     config.grid.autoWidth = false;
37437     
37438     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37439     
37440     if (config.background) {
37441         // render grid on panel activation (if panel background)
37442         this.on('activate', function(gp) {
37443             if (!gp.grid.rendered) {
37444                 gp.grid.render(this.wrapper);
37445                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37446             }
37447         });
37448             
37449     } else {
37450         this.grid.render(this.wrapper);
37451         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37452
37453     }
37454     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37455     // ??? needed ??? config.el = this.wrapper;
37456     
37457     
37458     
37459   
37460     // xtype created footer. - not sure if will work as we normally have to render first..
37461     if (this.footer && !this.footer.el && this.footer.xtype) {
37462         
37463         var ctr = this.grid.getView().getFooterPanel(true);
37464         this.footer.dataSource = this.grid.dataSource;
37465         this.footer = Roo.factory(this.footer, Roo);
37466         this.footer.render(ctr);
37467         
37468     }
37469     
37470     
37471     
37472     
37473      
37474 };
37475
37476 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37477     getId : function(){
37478         return this.grid.id;
37479     },
37480     
37481     /**
37482      * Returns the grid for this panel
37483      * @return {Roo.bootstrap.Table} 
37484      */
37485     getGrid : function(){
37486         return this.grid;    
37487     },
37488     
37489     setSize : function(width, height){
37490         if(!this.ignoreResize(width, height)){
37491             var grid = this.grid;
37492             var size = this.adjustForComponents(width, height);
37493             var gridel = grid.getGridEl();
37494             gridel.setSize(size.width, size.height);
37495             /*
37496             var thd = grid.getGridEl().select('thead',true).first();
37497             var tbd = grid.getGridEl().select('tbody', true).first();
37498             if (tbd) {
37499                 tbd.setSize(width, height - thd.getHeight());
37500             }
37501             */
37502             grid.autoSize();
37503         }
37504     },
37505      
37506     
37507     
37508     beforeSlide : function(){
37509         this.grid.getView().scroller.clip();
37510     },
37511     
37512     afterSlide : function(){
37513         this.grid.getView().scroller.unclip();
37514     },
37515     
37516     destroy : function(){
37517         this.grid.destroy();
37518         delete this.grid;
37519         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37520     }
37521 });
37522
37523 /**
37524  * @class Roo.bootstrap.panel.Nest
37525  * @extends Roo.bootstrap.panel.Content
37526  * @constructor
37527  * Create a new Panel, that can contain a layout.Border.
37528  * 
37529  * 
37530  * @param {Roo.BorderLayout} layout The layout for this panel
37531  * @param {String/Object} config A string to set only the title or a config object
37532  */
37533 Roo.bootstrap.panel.Nest = function(config)
37534 {
37535     // construct with only one argument..
37536     /* FIXME - implement nicer consturctors
37537     if (layout.layout) {
37538         config = layout;
37539         layout = config.layout;
37540         delete config.layout;
37541     }
37542     if (layout.xtype && !layout.getEl) {
37543         // then layout needs constructing..
37544         layout = Roo.factory(layout, Roo);
37545     }
37546     */
37547     
37548     config.el =  config.layout.getEl();
37549     
37550     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37551     
37552     config.layout.monitorWindowResize = false; // turn off autosizing
37553     this.layout = config.layout;
37554     this.layout.getEl().addClass("roo-layout-nested-layout");
37555     
37556     
37557     
37558     
37559 };
37560
37561 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37562
37563     setSize : function(width, height){
37564         if(!this.ignoreResize(width, height)){
37565             var size = this.adjustForComponents(width, height);
37566             var el = this.layout.getEl();
37567             if (size.height < 1) {
37568                 el.setWidth(size.width);   
37569             } else {
37570                 el.setSize(size.width, size.height);
37571             }
37572             var touch = el.dom.offsetWidth;
37573             this.layout.layout();
37574             // ie requires a double layout on the first pass
37575             if(Roo.isIE && !this.initialized){
37576                 this.initialized = true;
37577                 this.layout.layout();
37578             }
37579         }
37580     },
37581     
37582     // activate all subpanels if not currently active..
37583     
37584     setActiveState : function(active){
37585         this.active = active;
37586         this.setActiveClass(active);
37587         
37588         if(!active){
37589             this.fireEvent("deactivate", this);
37590             return;
37591         }
37592         
37593         this.fireEvent("activate", this);
37594         // not sure if this should happen before or after..
37595         if (!this.layout) {
37596             return; // should not happen..
37597         }
37598         var reg = false;
37599         for (var r in this.layout.regions) {
37600             reg = this.layout.getRegion(r);
37601             if (reg.getActivePanel()) {
37602                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37603                 reg.setActivePanel(reg.getActivePanel());
37604                 continue;
37605             }
37606             if (!reg.panels.length) {
37607                 continue;
37608             }
37609             reg.showPanel(reg.getPanel(0));
37610         }
37611         
37612         
37613         
37614         
37615     },
37616     
37617     /**
37618      * Returns the nested BorderLayout for this panel
37619      * @return {Roo.BorderLayout} 
37620      */
37621     getLayout : function(){
37622         return this.layout;
37623     },
37624     
37625      /**
37626      * Adds a xtype elements to the layout of the nested panel
37627      * <pre><code>
37628
37629 panel.addxtype({
37630        xtype : 'ContentPanel',
37631        region: 'west',
37632        items: [ .... ]
37633    }
37634 );
37635
37636 panel.addxtype({
37637         xtype : 'NestedLayoutPanel',
37638         region: 'west',
37639         layout: {
37640            center: { },
37641            west: { }   
37642         },
37643         items : [ ... list of content panels or nested layout panels.. ]
37644    }
37645 );
37646 </code></pre>
37647      * @param {Object} cfg Xtype definition of item to add.
37648      */
37649     addxtype : function(cfg) {
37650         return this.layout.addxtype(cfg);
37651     
37652     }
37653 });        /*
37654  * Based on:
37655  * Ext JS Library 1.1.1
37656  * Copyright(c) 2006-2007, Ext JS, LLC.
37657  *
37658  * Originally Released Under LGPL - original licence link has changed is not relivant.
37659  *
37660  * Fork - LGPL
37661  * <script type="text/javascript">
37662  */
37663 /**
37664  * @class Roo.TabPanel
37665  * @extends Roo.util.Observable
37666  * A lightweight tab container.
37667  * <br><br>
37668  * Usage:
37669  * <pre><code>
37670 // basic tabs 1, built from existing content
37671 var tabs = new Roo.TabPanel("tabs1");
37672 tabs.addTab("script", "View Script");
37673 tabs.addTab("markup", "View Markup");
37674 tabs.activate("script");
37675
37676 // more advanced tabs, built from javascript
37677 var jtabs = new Roo.TabPanel("jtabs");
37678 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37679
37680 // set up the UpdateManager
37681 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37682 var updater = tab2.getUpdateManager();
37683 updater.setDefaultUrl("ajax1.htm");
37684 tab2.on('activate', updater.refresh, updater, true);
37685
37686 // Use setUrl for Ajax loading
37687 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37688 tab3.setUrl("ajax2.htm", null, true);
37689
37690 // Disabled tab
37691 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37692 tab4.disable();
37693
37694 jtabs.activate("jtabs-1");
37695  * </code></pre>
37696  * @constructor
37697  * Create a new TabPanel.
37698  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37699  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37700  */
37701 Roo.bootstrap.panel.Tabs = function(config){
37702     /**
37703     * The container element for this TabPanel.
37704     * @type Roo.Element
37705     */
37706     this.el = Roo.get(config.el);
37707     delete config.el;
37708     if(config){
37709         if(typeof config == "boolean"){
37710             this.tabPosition = config ? "bottom" : "top";
37711         }else{
37712             Roo.apply(this, config);
37713         }
37714     }
37715     
37716     if(this.tabPosition == "bottom"){
37717         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37718         this.el.addClass("roo-tabs-bottom");
37719     }
37720     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37721     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37722     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37723     if(Roo.isIE){
37724         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37725     }
37726     if(this.tabPosition != "bottom"){
37727         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37728          * @type Roo.Element
37729          */
37730         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37731         this.el.addClass("roo-tabs-top");
37732     }
37733     this.items = [];
37734
37735     this.bodyEl.setStyle("position", "relative");
37736
37737     this.active = null;
37738     this.activateDelegate = this.activate.createDelegate(this);
37739
37740     this.addEvents({
37741         /**
37742          * @event tabchange
37743          * Fires when the active tab changes
37744          * @param {Roo.TabPanel} this
37745          * @param {Roo.TabPanelItem} activePanel The new active tab
37746          */
37747         "tabchange": true,
37748         /**
37749          * @event beforetabchange
37750          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37751          * @param {Roo.TabPanel} this
37752          * @param {Object} e Set cancel to true on this object to cancel the tab change
37753          * @param {Roo.TabPanelItem} tab The tab being changed to
37754          */
37755         "beforetabchange" : true
37756     });
37757
37758     Roo.EventManager.onWindowResize(this.onResize, this);
37759     this.cpad = this.el.getPadding("lr");
37760     this.hiddenCount = 0;
37761
37762
37763     // toolbar on the tabbar support...
37764     if (this.toolbar) {
37765         alert("no toolbar support yet");
37766         this.toolbar  = false;
37767         /*
37768         var tcfg = this.toolbar;
37769         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37770         this.toolbar = new Roo.Toolbar(tcfg);
37771         if (Roo.isSafari) {
37772             var tbl = tcfg.container.child('table', true);
37773             tbl.setAttribute('width', '100%');
37774         }
37775         */
37776         
37777     }
37778    
37779
37780
37781     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37782 };
37783
37784 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37785     /*
37786      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37787      */
37788     tabPosition : "top",
37789     /*
37790      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37791      */
37792     currentTabWidth : 0,
37793     /*
37794      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37795      */
37796     minTabWidth : 40,
37797     /*
37798      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37799      */
37800     maxTabWidth : 250,
37801     /*
37802      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37803      */
37804     preferredTabWidth : 175,
37805     /*
37806      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37807      */
37808     resizeTabs : false,
37809     /*
37810      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37811      */
37812     monitorResize : true,
37813     /*
37814      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37815      */
37816     toolbar : false,
37817
37818     /**
37819      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37820      * @param {String} id The id of the div to use <b>or create</b>
37821      * @param {String} text The text for the tab
37822      * @param {String} content (optional) Content to put in the TabPanelItem body
37823      * @param {Boolean} closable (optional) True to create a close icon on the tab
37824      * @return {Roo.TabPanelItem} The created TabPanelItem
37825      */
37826     addTab : function(id, text, content, closable, tpl)
37827     {
37828         var item = new Roo.bootstrap.panel.TabItem({
37829             panel: this,
37830             id : id,
37831             text : text,
37832             closable : closable,
37833             tpl : tpl
37834         });
37835         this.addTabItem(item);
37836         if(content){
37837             item.setContent(content);
37838         }
37839         return item;
37840     },
37841
37842     /**
37843      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37844      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37845      * @return {Roo.TabPanelItem}
37846      */
37847     getTab : function(id){
37848         return this.items[id];
37849     },
37850
37851     /**
37852      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37853      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37854      */
37855     hideTab : function(id){
37856         var t = this.items[id];
37857         if(!t.isHidden()){
37858            t.setHidden(true);
37859            this.hiddenCount++;
37860            this.autoSizeTabs();
37861         }
37862     },
37863
37864     /**
37865      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37866      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37867      */
37868     unhideTab : function(id){
37869         var t = this.items[id];
37870         if(t.isHidden()){
37871            t.setHidden(false);
37872            this.hiddenCount--;
37873            this.autoSizeTabs();
37874         }
37875     },
37876
37877     /**
37878      * Adds an existing {@link Roo.TabPanelItem}.
37879      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37880      */
37881     addTabItem : function(item){
37882         this.items[item.id] = item;
37883         this.items.push(item);
37884       //  if(this.resizeTabs){
37885     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37886   //         this.autoSizeTabs();
37887 //        }else{
37888 //            item.autoSize();
37889        // }
37890     },
37891
37892     /**
37893      * Removes a {@link Roo.TabPanelItem}.
37894      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37895      */
37896     removeTab : function(id){
37897         var items = this.items;
37898         var tab = items[id];
37899         if(!tab) { return; }
37900         var index = items.indexOf(tab);
37901         if(this.active == tab && items.length > 1){
37902             var newTab = this.getNextAvailable(index);
37903             if(newTab) {
37904                 newTab.activate();
37905             }
37906         }
37907         this.stripEl.dom.removeChild(tab.pnode.dom);
37908         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37909             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37910         }
37911         items.splice(index, 1);
37912         delete this.items[tab.id];
37913         tab.fireEvent("close", tab);
37914         tab.purgeListeners();
37915         this.autoSizeTabs();
37916     },
37917
37918     getNextAvailable : function(start){
37919         var items = this.items;
37920         var index = start;
37921         // look for a next tab that will slide over to
37922         // replace the one being removed
37923         while(index < items.length){
37924             var item = items[++index];
37925             if(item && !item.isHidden()){
37926                 return item;
37927             }
37928         }
37929         // if one isn't found select the previous tab (on the left)
37930         index = start;
37931         while(index >= 0){
37932             var item = items[--index];
37933             if(item && !item.isHidden()){
37934                 return item;
37935             }
37936         }
37937         return null;
37938     },
37939
37940     /**
37941      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37942      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37943      */
37944     disableTab : function(id){
37945         var tab = this.items[id];
37946         if(tab && this.active != tab){
37947             tab.disable();
37948         }
37949     },
37950
37951     /**
37952      * Enables a {@link Roo.TabPanelItem} that is disabled.
37953      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37954      */
37955     enableTab : function(id){
37956         var tab = this.items[id];
37957         tab.enable();
37958     },
37959
37960     /**
37961      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37962      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37963      * @return {Roo.TabPanelItem} The TabPanelItem.
37964      */
37965     activate : function(id){
37966         var tab = this.items[id];
37967         if(!tab){
37968             return null;
37969         }
37970         if(tab == this.active || tab.disabled){
37971             return tab;
37972         }
37973         var e = {};
37974         this.fireEvent("beforetabchange", this, e, tab);
37975         if(e.cancel !== true && !tab.disabled){
37976             if(this.active){
37977                 this.active.hide();
37978             }
37979             this.active = this.items[id];
37980             this.active.show();
37981             this.fireEvent("tabchange", this, this.active);
37982         }
37983         return tab;
37984     },
37985
37986     /**
37987      * Gets the active {@link Roo.TabPanelItem}.
37988      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37989      */
37990     getActiveTab : function(){
37991         return this.active;
37992     },
37993
37994     /**
37995      * Updates the tab body element to fit the height of the container element
37996      * for overflow scrolling
37997      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37998      */
37999     syncHeight : function(targetHeight){
38000         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38001         var bm = this.bodyEl.getMargins();
38002         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38003         this.bodyEl.setHeight(newHeight);
38004         return newHeight;
38005     },
38006
38007     onResize : function(){
38008         if(this.monitorResize){
38009             this.autoSizeTabs();
38010         }
38011     },
38012
38013     /**
38014      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38015      */
38016     beginUpdate : function(){
38017         this.updating = true;
38018     },
38019
38020     /**
38021      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38022      */
38023     endUpdate : function(){
38024         this.updating = false;
38025         this.autoSizeTabs();
38026     },
38027
38028     /**
38029      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38030      */
38031     autoSizeTabs : function(){
38032         var count = this.items.length;
38033         var vcount = count - this.hiddenCount;
38034         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38035             return;
38036         }
38037         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38038         var availWidth = Math.floor(w / vcount);
38039         var b = this.stripBody;
38040         if(b.getWidth() > w){
38041             var tabs = this.items;
38042             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38043             if(availWidth < this.minTabWidth){
38044                 /*if(!this.sleft){    // incomplete scrolling code
38045                     this.createScrollButtons();
38046                 }
38047                 this.showScroll();
38048                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38049             }
38050         }else{
38051             if(this.currentTabWidth < this.preferredTabWidth){
38052                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38053             }
38054         }
38055     },
38056
38057     /**
38058      * Returns the number of tabs in this TabPanel.
38059      * @return {Number}
38060      */
38061      getCount : function(){
38062          return this.items.length;
38063      },
38064
38065     /**
38066      * Resizes all the tabs to the passed width
38067      * @param {Number} The new width
38068      */
38069     setTabWidth : function(width){
38070         this.currentTabWidth = width;
38071         for(var i = 0, len = this.items.length; i < len; i++) {
38072                 if(!this.items[i].isHidden()) {
38073                 this.items[i].setWidth(width);
38074             }
38075         }
38076     },
38077
38078     /**
38079      * Destroys this TabPanel
38080      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38081      */
38082     destroy : function(removeEl){
38083         Roo.EventManager.removeResizeListener(this.onResize, this);
38084         for(var i = 0, len = this.items.length; i < len; i++){
38085             this.items[i].purgeListeners();
38086         }
38087         if(removeEl === true){
38088             this.el.update("");
38089             this.el.remove();
38090         }
38091     },
38092     
38093     createStrip : function(container)
38094     {
38095         var strip = document.createElement("nav");
38096         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38097         container.appendChild(strip);
38098         return strip;
38099     },
38100     
38101     createStripList : function(strip)
38102     {
38103         // div wrapper for retard IE
38104         // returns the "tr" element.
38105         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38106         //'<div class="x-tabs-strip-wrap">'+
38107           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38108           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38109         return strip.firstChild; //.firstChild.firstChild.firstChild;
38110     },
38111     createBody : function(container)
38112     {
38113         var body = document.createElement("div");
38114         Roo.id(body, "tab-body");
38115         //Roo.fly(body).addClass("x-tabs-body");
38116         Roo.fly(body).addClass("tab-content");
38117         container.appendChild(body);
38118         return body;
38119     },
38120     createItemBody :function(bodyEl, id){
38121         var body = Roo.getDom(id);
38122         if(!body){
38123             body = document.createElement("div");
38124             body.id = id;
38125         }
38126         //Roo.fly(body).addClass("x-tabs-item-body");
38127         Roo.fly(body).addClass("tab-pane");
38128          bodyEl.insertBefore(body, bodyEl.firstChild);
38129         return body;
38130     },
38131     /** @private */
38132     createStripElements :  function(stripEl, text, closable, tpl)
38133     {
38134         var td = document.createElement("li"); // was td..
38135         
38136         
38137         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38138         
38139         
38140         stripEl.appendChild(td);
38141         /*if(closable){
38142             td.className = "x-tabs-closable";
38143             if(!this.closeTpl){
38144                 this.closeTpl = new Roo.Template(
38145                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38146                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38147                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38148                 );
38149             }
38150             var el = this.closeTpl.overwrite(td, {"text": text});
38151             var close = el.getElementsByTagName("div")[0];
38152             var inner = el.getElementsByTagName("em")[0];
38153             return {"el": el, "close": close, "inner": inner};
38154         } else {
38155         */
38156         // not sure what this is..
38157 //            if(!this.tabTpl){
38158                 //this.tabTpl = new Roo.Template(
38159                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38160                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38161                 //);
38162 //                this.tabTpl = new Roo.Template(
38163 //                   '<a href="#">' +
38164 //                   '<span unselectable="on"' +
38165 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38166 //                            ' >{text}</span></a>'
38167 //                );
38168 //                
38169 //            }
38170
38171
38172             var template = tpl || this.tabTpl || false;
38173             
38174             if(!template){
38175                 
38176                 template = new Roo.Template(
38177                    '<a href="#">' +
38178                    '<span unselectable="on"' +
38179                             (this.disableTooltips ? '' : ' title="{text}"') +
38180                             ' >{text}</span></a>'
38181                 );
38182             }
38183             
38184             switch (typeof(template)) {
38185                 case 'object' :
38186                     break;
38187                 case 'string' :
38188                     template = new Roo.Template(template);
38189                     break;
38190                 default :
38191                     break;
38192             }
38193             
38194             var el = template.overwrite(td, {"text": text});
38195             
38196             var inner = el.getElementsByTagName("span")[0];
38197             
38198             return {"el": el, "inner": inner};
38199             
38200     }
38201         
38202     
38203 });
38204
38205 /**
38206  * @class Roo.TabPanelItem
38207  * @extends Roo.util.Observable
38208  * Represents an individual item (tab plus body) in a TabPanel.
38209  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38210  * @param {String} id The id of this TabPanelItem
38211  * @param {String} text The text for the tab of this TabPanelItem
38212  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38213  */
38214 Roo.bootstrap.panel.TabItem = function(config){
38215     /**
38216      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38217      * @type Roo.TabPanel
38218      */
38219     this.tabPanel = config.panel;
38220     /**
38221      * The id for this TabPanelItem
38222      * @type String
38223      */
38224     this.id = config.id;
38225     /** @private */
38226     this.disabled = false;
38227     /** @private */
38228     this.text = config.text;
38229     /** @private */
38230     this.loaded = false;
38231     this.closable = config.closable;
38232
38233     /**
38234      * The body element for this TabPanelItem.
38235      * @type Roo.Element
38236      */
38237     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38238     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38239     this.bodyEl.setStyle("display", "block");
38240     this.bodyEl.setStyle("zoom", "1");
38241     //this.hideAction();
38242
38243     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38244     /** @private */
38245     this.el = Roo.get(els.el);
38246     this.inner = Roo.get(els.inner, true);
38247     this.textEl = Roo.get(this.el.dom.firstChild, true);
38248     this.pnode = Roo.get(els.el.parentNode, true);
38249 //    this.el.on("mousedown", this.onTabMouseDown, this);
38250     this.el.on("click", this.onTabClick, this);
38251     /** @private */
38252     if(config.closable){
38253         var c = Roo.get(els.close, true);
38254         c.dom.title = this.closeText;
38255         c.addClassOnOver("close-over");
38256         c.on("click", this.closeClick, this);
38257      }
38258
38259     this.addEvents({
38260          /**
38261          * @event activate
38262          * Fires when this tab becomes the active tab.
38263          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38264          * @param {Roo.TabPanelItem} this
38265          */
38266         "activate": true,
38267         /**
38268          * @event beforeclose
38269          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38270          * @param {Roo.TabPanelItem} this
38271          * @param {Object} e Set cancel to true on this object to cancel the close.
38272          */
38273         "beforeclose": true,
38274         /**
38275          * @event close
38276          * Fires when this tab is closed.
38277          * @param {Roo.TabPanelItem} this
38278          */
38279          "close": true,
38280         /**
38281          * @event deactivate
38282          * Fires when this tab is no longer the active tab.
38283          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38284          * @param {Roo.TabPanelItem} this
38285          */
38286          "deactivate" : true
38287     });
38288     this.hidden = false;
38289
38290     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38291 };
38292
38293 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38294            {
38295     purgeListeners : function(){
38296        Roo.util.Observable.prototype.purgeListeners.call(this);
38297        this.el.removeAllListeners();
38298     },
38299     /**
38300      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38301      */
38302     show : function(){
38303         this.pnode.addClass("active");
38304         this.showAction();
38305         if(Roo.isOpera){
38306             this.tabPanel.stripWrap.repaint();
38307         }
38308         this.fireEvent("activate", this.tabPanel, this);
38309     },
38310
38311     /**
38312      * Returns true if this tab is the active tab.
38313      * @return {Boolean}
38314      */
38315     isActive : function(){
38316         return this.tabPanel.getActiveTab() == this;
38317     },
38318
38319     /**
38320      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38321      */
38322     hide : function(){
38323         this.pnode.removeClass("active");
38324         this.hideAction();
38325         this.fireEvent("deactivate", this.tabPanel, this);
38326     },
38327
38328     hideAction : function(){
38329         this.bodyEl.hide();
38330         this.bodyEl.setStyle("position", "absolute");
38331         this.bodyEl.setLeft("-20000px");
38332         this.bodyEl.setTop("-20000px");
38333     },
38334
38335     showAction : function(){
38336         this.bodyEl.setStyle("position", "relative");
38337         this.bodyEl.setTop("");
38338         this.bodyEl.setLeft("");
38339         this.bodyEl.show();
38340     },
38341
38342     /**
38343      * Set the tooltip for the tab.
38344      * @param {String} tooltip The tab's tooltip
38345      */
38346     setTooltip : function(text){
38347         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38348             this.textEl.dom.qtip = text;
38349             this.textEl.dom.removeAttribute('title');
38350         }else{
38351             this.textEl.dom.title = text;
38352         }
38353     },
38354
38355     onTabClick : function(e){
38356         e.preventDefault();
38357         this.tabPanel.activate(this.id);
38358     },
38359
38360     onTabMouseDown : function(e){
38361         e.preventDefault();
38362         this.tabPanel.activate(this.id);
38363     },
38364 /*
38365     getWidth : function(){
38366         return this.inner.getWidth();
38367     },
38368
38369     setWidth : function(width){
38370         var iwidth = width - this.pnode.getPadding("lr");
38371         this.inner.setWidth(iwidth);
38372         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38373         this.pnode.setWidth(width);
38374     },
38375 */
38376     /**
38377      * Show or hide the tab
38378      * @param {Boolean} hidden True to hide or false to show.
38379      */
38380     setHidden : function(hidden){
38381         this.hidden = hidden;
38382         this.pnode.setStyle("display", hidden ? "none" : "");
38383     },
38384
38385     /**
38386      * Returns true if this tab is "hidden"
38387      * @return {Boolean}
38388      */
38389     isHidden : function(){
38390         return this.hidden;
38391     },
38392
38393     /**
38394      * Returns the text for this tab
38395      * @return {String}
38396      */
38397     getText : function(){
38398         return this.text;
38399     },
38400     /*
38401     autoSize : function(){
38402         //this.el.beginMeasure();
38403         this.textEl.setWidth(1);
38404         /*
38405          *  #2804 [new] Tabs in Roojs
38406          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38407          */
38408         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38409         //this.el.endMeasure();
38410     //},
38411
38412     /**
38413      * Sets the text for the tab (Note: this also sets the tooltip text)
38414      * @param {String} text The tab's text and tooltip
38415      */
38416     setText : function(text){
38417         this.text = text;
38418         this.textEl.update(text);
38419         this.setTooltip(text);
38420         //if(!this.tabPanel.resizeTabs){
38421         //    this.autoSize();
38422         //}
38423     },
38424     /**
38425      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38426      */
38427     activate : function(){
38428         this.tabPanel.activate(this.id);
38429     },
38430
38431     /**
38432      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38433      */
38434     disable : function(){
38435         if(this.tabPanel.active != this){
38436             this.disabled = true;
38437             this.pnode.addClass("disabled");
38438         }
38439     },
38440
38441     /**
38442      * Enables this TabPanelItem if it was previously disabled.
38443      */
38444     enable : function(){
38445         this.disabled = false;
38446         this.pnode.removeClass("disabled");
38447     },
38448
38449     /**
38450      * Sets the content for this TabPanelItem.
38451      * @param {String} content The content
38452      * @param {Boolean} loadScripts true to look for and load scripts
38453      */
38454     setContent : function(content, loadScripts){
38455         this.bodyEl.update(content, loadScripts);
38456     },
38457
38458     /**
38459      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38460      * @return {Roo.UpdateManager} The UpdateManager
38461      */
38462     getUpdateManager : function(){
38463         return this.bodyEl.getUpdateManager();
38464     },
38465
38466     /**
38467      * Set a URL to be used to load the content for this TabPanelItem.
38468      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38469      * @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)
38470      * @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)
38471      * @return {Roo.UpdateManager} The UpdateManager
38472      */
38473     setUrl : function(url, params, loadOnce){
38474         if(this.refreshDelegate){
38475             this.un('activate', this.refreshDelegate);
38476         }
38477         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38478         this.on("activate", this.refreshDelegate);
38479         return this.bodyEl.getUpdateManager();
38480     },
38481
38482     /** @private */
38483     _handleRefresh : function(url, params, loadOnce){
38484         if(!loadOnce || !this.loaded){
38485             var updater = this.bodyEl.getUpdateManager();
38486             updater.update(url, params, this._setLoaded.createDelegate(this));
38487         }
38488     },
38489
38490     /**
38491      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38492      *   Will fail silently if the setUrl method has not been called.
38493      *   This does not activate the panel, just updates its content.
38494      */
38495     refresh : function(){
38496         if(this.refreshDelegate){
38497            this.loaded = false;
38498            this.refreshDelegate();
38499         }
38500     },
38501
38502     /** @private */
38503     _setLoaded : function(){
38504         this.loaded = true;
38505     },
38506
38507     /** @private */
38508     closeClick : function(e){
38509         var o = {};
38510         e.stopEvent();
38511         this.fireEvent("beforeclose", this, o);
38512         if(o.cancel !== true){
38513             this.tabPanel.removeTab(this.id);
38514         }
38515     },
38516     /**
38517      * The text displayed in the tooltip for the close icon.
38518      * @type String
38519      */
38520     closeText : "Close this tab"
38521 });
38522 /**
38523 *    This script refer to:
38524 *    Title: International Telephone Input
38525 *    Author: Jack O'Connor
38526 *    Code version:  v12.1.12
38527 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38528 **/
38529
38530 Roo.bootstrap.PhoneInputData = function() {
38531     var d = [
38532       [
38533         "Afghanistan (‫افغانستان‬‎)",
38534         "af",
38535         "93"
38536       ],
38537       [
38538         "Albania (Shqipëri)",
38539         "al",
38540         "355"
38541       ],
38542       [
38543         "Algeria (‫الجزائر‬‎)",
38544         "dz",
38545         "213"
38546       ],
38547       [
38548         "American Samoa",
38549         "as",
38550         "1684"
38551       ],
38552       [
38553         "Andorra",
38554         "ad",
38555         "376"
38556       ],
38557       [
38558         "Angola",
38559         "ao",
38560         "244"
38561       ],
38562       [
38563         "Anguilla",
38564         "ai",
38565         "1264"
38566       ],
38567       [
38568         "Antigua and Barbuda",
38569         "ag",
38570         "1268"
38571       ],
38572       [
38573         "Argentina",
38574         "ar",
38575         "54"
38576       ],
38577       [
38578         "Armenia (Հայաստան)",
38579         "am",
38580         "374"
38581       ],
38582       [
38583         "Aruba",
38584         "aw",
38585         "297"
38586       ],
38587       [
38588         "Australia",
38589         "au",
38590         "61",
38591         0
38592       ],
38593       [
38594         "Austria (Österreich)",
38595         "at",
38596         "43"
38597       ],
38598       [
38599         "Azerbaijan (Azərbaycan)",
38600         "az",
38601         "994"
38602       ],
38603       [
38604         "Bahamas",
38605         "bs",
38606         "1242"
38607       ],
38608       [
38609         "Bahrain (‫البحرين‬‎)",
38610         "bh",
38611         "973"
38612       ],
38613       [
38614         "Bangladesh (বাংলাদেশ)",
38615         "bd",
38616         "880"
38617       ],
38618       [
38619         "Barbados",
38620         "bb",
38621         "1246"
38622       ],
38623       [
38624         "Belarus (Беларусь)",
38625         "by",
38626         "375"
38627       ],
38628       [
38629         "Belgium (België)",
38630         "be",
38631         "32"
38632       ],
38633       [
38634         "Belize",
38635         "bz",
38636         "501"
38637       ],
38638       [
38639         "Benin (Bénin)",
38640         "bj",
38641         "229"
38642       ],
38643       [
38644         "Bermuda",
38645         "bm",
38646         "1441"
38647       ],
38648       [
38649         "Bhutan (འབྲུག)",
38650         "bt",
38651         "975"
38652       ],
38653       [
38654         "Bolivia",
38655         "bo",
38656         "591"
38657       ],
38658       [
38659         "Bosnia and Herzegovina (Босна и Херцеговина)",
38660         "ba",
38661         "387"
38662       ],
38663       [
38664         "Botswana",
38665         "bw",
38666         "267"
38667       ],
38668       [
38669         "Brazil (Brasil)",
38670         "br",
38671         "55"
38672       ],
38673       [
38674         "British Indian Ocean Territory",
38675         "io",
38676         "246"
38677       ],
38678       [
38679         "British Virgin Islands",
38680         "vg",
38681         "1284"
38682       ],
38683       [
38684         "Brunei",
38685         "bn",
38686         "673"
38687       ],
38688       [
38689         "Bulgaria (България)",
38690         "bg",
38691         "359"
38692       ],
38693       [
38694         "Burkina Faso",
38695         "bf",
38696         "226"
38697       ],
38698       [
38699         "Burundi (Uburundi)",
38700         "bi",
38701         "257"
38702       ],
38703       [
38704         "Cambodia (កម្ពុជា)",
38705         "kh",
38706         "855"
38707       ],
38708       [
38709         "Cameroon (Cameroun)",
38710         "cm",
38711         "237"
38712       ],
38713       [
38714         "Canada",
38715         "ca",
38716         "1",
38717         1,
38718         ["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"]
38719       ],
38720       [
38721         "Cape Verde (Kabu Verdi)",
38722         "cv",
38723         "238"
38724       ],
38725       [
38726         "Caribbean Netherlands",
38727         "bq",
38728         "599",
38729         1
38730       ],
38731       [
38732         "Cayman Islands",
38733         "ky",
38734         "1345"
38735       ],
38736       [
38737         "Central African Republic (République centrafricaine)",
38738         "cf",
38739         "236"
38740       ],
38741       [
38742         "Chad (Tchad)",
38743         "td",
38744         "235"
38745       ],
38746       [
38747         "Chile",
38748         "cl",
38749         "56"
38750       ],
38751       [
38752         "China (中国)",
38753         "cn",
38754         "86"
38755       ],
38756       [
38757         "Christmas Island",
38758         "cx",
38759         "61",
38760         2
38761       ],
38762       [
38763         "Cocos (Keeling) Islands",
38764         "cc",
38765         "61",
38766         1
38767       ],
38768       [
38769         "Colombia",
38770         "co",
38771         "57"
38772       ],
38773       [
38774         "Comoros (‫جزر القمر‬‎)",
38775         "km",
38776         "269"
38777       ],
38778       [
38779         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38780         "cd",
38781         "243"
38782       ],
38783       [
38784         "Congo (Republic) (Congo-Brazzaville)",
38785         "cg",
38786         "242"
38787       ],
38788       [
38789         "Cook Islands",
38790         "ck",
38791         "682"
38792       ],
38793       [
38794         "Costa Rica",
38795         "cr",
38796         "506"
38797       ],
38798       [
38799         "Côte d’Ivoire",
38800         "ci",
38801         "225"
38802       ],
38803       [
38804         "Croatia (Hrvatska)",
38805         "hr",
38806         "385"
38807       ],
38808       [
38809         "Cuba",
38810         "cu",
38811         "53"
38812       ],
38813       [
38814         "Curaçao",
38815         "cw",
38816         "599",
38817         0
38818       ],
38819       [
38820         "Cyprus (Κύπρος)",
38821         "cy",
38822         "357"
38823       ],
38824       [
38825         "Czech Republic (Česká republika)",
38826         "cz",
38827         "420"
38828       ],
38829       [
38830         "Denmark (Danmark)",
38831         "dk",
38832         "45"
38833       ],
38834       [
38835         "Djibouti",
38836         "dj",
38837         "253"
38838       ],
38839       [
38840         "Dominica",
38841         "dm",
38842         "1767"
38843       ],
38844       [
38845         "Dominican Republic (República Dominicana)",
38846         "do",
38847         "1",
38848         2,
38849         ["809", "829", "849"]
38850       ],
38851       [
38852         "Ecuador",
38853         "ec",
38854         "593"
38855       ],
38856       [
38857         "Egypt (‫مصر‬‎)",
38858         "eg",
38859         "20"
38860       ],
38861       [
38862         "El Salvador",
38863         "sv",
38864         "503"
38865       ],
38866       [
38867         "Equatorial Guinea (Guinea Ecuatorial)",
38868         "gq",
38869         "240"
38870       ],
38871       [
38872         "Eritrea",
38873         "er",
38874         "291"
38875       ],
38876       [
38877         "Estonia (Eesti)",
38878         "ee",
38879         "372"
38880       ],
38881       [
38882         "Ethiopia",
38883         "et",
38884         "251"
38885       ],
38886       [
38887         "Falkland Islands (Islas Malvinas)",
38888         "fk",
38889         "500"
38890       ],
38891       [
38892         "Faroe Islands (Føroyar)",
38893         "fo",
38894         "298"
38895       ],
38896       [
38897         "Fiji",
38898         "fj",
38899         "679"
38900       ],
38901       [
38902         "Finland (Suomi)",
38903         "fi",
38904         "358",
38905         0
38906       ],
38907       [
38908         "France",
38909         "fr",
38910         "33"
38911       ],
38912       [
38913         "French Guiana (Guyane française)",
38914         "gf",
38915         "594"
38916       ],
38917       [
38918         "French Polynesia (Polynésie française)",
38919         "pf",
38920         "689"
38921       ],
38922       [
38923         "Gabon",
38924         "ga",
38925         "241"
38926       ],
38927       [
38928         "Gambia",
38929         "gm",
38930         "220"
38931       ],
38932       [
38933         "Georgia (საქართველო)",
38934         "ge",
38935         "995"
38936       ],
38937       [
38938         "Germany (Deutschland)",
38939         "de",
38940         "49"
38941       ],
38942       [
38943         "Ghana (Gaana)",
38944         "gh",
38945         "233"
38946       ],
38947       [
38948         "Gibraltar",
38949         "gi",
38950         "350"
38951       ],
38952       [
38953         "Greece (Ελλάδα)",
38954         "gr",
38955         "30"
38956       ],
38957       [
38958         "Greenland (Kalaallit Nunaat)",
38959         "gl",
38960         "299"
38961       ],
38962       [
38963         "Grenada",
38964         "gd",
38965         "1473"
38966       ],
38967       [
38968         "Guadeloupe",
38969         "gp",
38970         "590",
38971         0
38972       ],
38973       [
38974         "Guam",
38975         "gu",
38976         "1671"
38977       ],
38978       [
38979         "Guatemala",
38980         "gt",
38981         "502"
38982       ],
38983       [
38984         "Guernsey",
38985         "gg",
38986         "44",
38987         1
38988       ],
38989       [
38990         "Guinea (Guinée)",
38991         "gn",
38992         "224"
38993       ],
38994       [
38995         "Guinea-Bissau (Guiné Bissau)",
38996         "gw",
38997         "245"
38998       ],
38999       [
39000         "Guyana",
39001         "gy",
39002         "592"
39003       ],
39004       [
39005         "Haiti",
39006         "ht",
39007         "509"
39008       ],
39009       [
39010         "Honduras",
39011         "hn",
39012         "504"
39013       ],
39014       [
39015         "Hong Kong (香港)",
39016         "hk",
39017         "852"
39018       ],
39019       [
39020         "Hungary (Magyarország)",
39021         "hu",
39022         "36"
39023       ],
39024       [
39025         "Iceland (Ísland)",
39026         "is",
39027         "354"
39028       ],
39029       [
39030         "India (भारत)",
39031         "in",
39032         "91"
39033       ],
39034       [
39035         "Indonesia",
39036         "id",
39037         "62"
39038       ],
39039       [
39040         "Iran (‫ایران‬‎)",
39041         "ir",
39042         "98"
39043       ],
39044       [
39045         "Iraq (‫العراق‬‎)",
39046         "iq",
39047         "964"
39048       ],
39049       [
39050         "Ireland",
39051         "ie",
39052         "353"
39053       ],
39054       [
39055         "Isle of Man",
39056         "im",
39057         "44",
39058         2
39059       ],
39060       [
39061         "Israel (‫ישראל‬‎)",
39062         "il",
39063         "972"
39064       ],
39065       [
39066         "Italy (Italia)",
39067         "it",
39068         "39",
39069         0
39070       ],
39071       [
39072         "Jamaica",
39073         "jm",
39074         "1876"
39075       ],
39076       [
39077         "Japan (日本)",
39078         "jp",
39079         "81"
39080       ],
39081       [
39082         "Jersey",
39083         "je",
39084         "44",
39085         3
39086       ],
39087       [
39088         "Jordan (‫الأردن‬‎)",
39089         "jo",
39090         "962"
39091       ],
39092       [
39093         "Kazakhstan (Казахстан)",
39094         "kz",
39095         "7",
39096         1
39097       ],
39098       [
39099         "Kenya",
39100         "ke",
39101         "254"
39102       ],
39103       [
39104         "Kiribati",
39105         "ki",
39106         "686"
39107       ],
39108       [
39109         "Kosovo",
39110         "xk",
39111         "383"
39112       ],
39113       [
39114         "Kuwait (‫الكويت‬‎)",
39115         "kw",
39116         "965"
39117       ],
39118       [
39119         "Kyrgyzstan (Кыргызстан)",
39120         "kg",
39121         "996"
39122       ],
39123       [
39124         "Laos (ລາວ)",
39125         "la",
39126         "856"
39127       ],
39128       [
39129         "Latvia (Latvija)",
39130         "lv",
39131         "371"
39132       ],
39133       [
39134         "Lebanon (‫لبنان‬‎)",
39135         "lb",
39136         "961"
39137       ],
39138       [
39139         "Lesotho",
39140         "ls",
39141         "266"
39142       ],
39143       [
39144         "Liberia",
39145         "lr",
39146         "231"
39147       ],
39148       [
39149         "Libya (‫ليبيا‬‎)",
39150         "ly",
39151         "218"
39152       ],
39153       [
39154         "Liechtenstein",
39155         "li",
39156         "423"
39157       ],
39158       [
39159         "Lithuania (Lietuva)",
39160         "lt",
39161         "370"
39162       ],
39163       [
39164         "Luxembourg",
39165         "lu",
39166         "352"
39167       ],
39168       [
39169         "Macau (澳門)",
39170         "mo",
39171         "853"
39172       ],
39173       [
39174         "Macedonia (FYROM) (Македонија)",
39175         "mk",
39176         "389"
39177       ],
39178       [
39179         "Madagascar (Madagasikara)",
39180         "mg",
39181         "261"
39182       ],
39183       [
39184         "Malawi",
39185         "mw",
39186         "265"
39187       ],
39188       [
39189         "Malaysia",
39190         "my",
39191         "60"
39192       ],
39193       [
39194         "Maldives",
39195         "mv",
39196         "960"
39197       ],
39198       [
39199         "Mali",
39200         "ml",
39201         "223"
39202       ],
39203       [
39204         "Malta",
39205         "mt",
39206         "356"
39207       ],
39208       [
39209         "Marshall Islands",
39210         "mh",
39211         "692"
39212       ],
39213       [
39214         "Martinique",
39215         "mq",
39216         "596"
39217       ],
39218       [
39219         "Mauritania (‫موريتانيا‬‎)",
39220         "mr",
39221         "222"
39222       ],
39223       [
39224         "Mauritius (Moris)",
39225         "mu",
39226         "230"
39227       ],
39228       [
39229         "Mayotte",
39230         "yt",
39231         "262",
39232         1
39233       ],
39234       [
39235         "Mexico (México)",
39236         "mx",
39237         "52"
39238       ],
39239       [
39240         "Micronesia",
39241         "fm",
39242         "691"
39243       ],
39244       [
39245         "Moldova (Republica Moldova)",
39246         "md",
39247         "373"
39248       ],
39249       [
39250         "Monaco",
39251         "mc",
39252         "377"
39253       ],
39254       [
39255         "Mongolia (Монгол)",
39256         "mn",
39257         "976"
39258       ],
39259       [
39260         "Montenegro (Crna Gora)",
39261         "me",
39262         "382"
39263       ],
39264       [
39265         "Montserrat",
39266         "ms",
39267         "1664"
39268       ],
39269       [
39270         "Morocco (‫المغرب‬‎)",
39271         "ma",
39272         "212",
39273         0
39274       ],
39275       [
39276         "Mozambique (Moçambique)",
39277         "mz",
39278         "258"
39279       ],
39280       [
39281         "Myanmar (Burma) (မြန်မာ)",
39282         "mm",
39283         "95"
39284       ],
39285       [
39286         "Namibia (Namibië)",
39287         "na",
39288         "264"
39289       ],
39290       [
39291         "Nauru",
39292         "nr",
39293         "674"
39294       ],
39295       [
39296         "Nepal (नेपाल)",
39297         "np",
39298         "977"
39299       ],
39300       [
39301         "Netherlands (Nederland)",
39302         "nl",
39303         "31"
39304       ],
39305       [
39306         "New Caledonia (Nouvelle-Calédonie)",
39307         "nc",
39308         "687"
39309       ],
39310       [
39311         "New Zealand",
39312         "nz",
39313         "64"
39314       ],
39315       [
39316         "Nicaragua",
39317         "ni",
39318         "505"
39319       ],
39320       [
39321         "Niger (Nijar)",
39322         "ne",
39323         "227"
39324       ],
39325       [
39326         "Nigeria",
39327         "ng",
39328         "234"
39329       ],
39330       [
39331         "Niue",
39332         "nu",
39333         "683"
39334       ],
39335       [
39336         "Norfolk Island",
39337         "nf",
39338         "672"
39339       ],
39340       [
39341         "North Korea (조선 민주주의 인민 공화국)",
39342         "kp",
39343         "850"
39344       ],
39345       [
39346         "Northern Mariana Islands",
39347         "mp",
39348         "1670"
39349       ],
39350       [
39351         "Norway (Norge)",
39352         "no",
39353         "47",
39354         0
39355       ],
39356       [
39357         "Oman (‫عُمان‬‎)",
39358         "om",
39359         "968"
39360       ],
39361       [
39362         "Pakistan (‫پاکستان‬‎)",
39363         "pk",
39364         "92"
39365       ],
39366       [
39367         "Palau",
39368         "pw",
39369         "680"
39370       ],
39371       [
39372         "Palestine (‫فلسطين‬‎)",
39373         "ps",
39374         "970"
39375       ],
39376       [
39377         "Panama (Panamá)",
39378         "pa",
39379         "507"
39380       ],
39381       [
39382         "Papua New Guinea",
39383         "pg",
39384         "675"
39385       ],
39386       [
39387         "Paraguay",
39388         "py",
39389         "595"
39390       ],
39391       [
39392         "Peru (Perú)",
39393         "pe",
39394         "51"
39395       ],
39396       [
39397         "Philippines",
39398         "ph",
39399         "63"
39400       ],
39401       [
39402         "Poland (Polska)",
39403         "pl",
39404         "48"
39405       ],
39406       [
39407         "Portugal",
39408         "pt",
39409         "351"
39410       ],
39411       [
39412         "Puerto Rico",
39413         "pr",
39414         "1",
39415         3,
39416         ["787", "939"]
39417       ],
39418       [
39419         "Qatar (‫قطر‬‎)",
39420         "qa",
39421         "974"
39422       ],
39423       [
39424         "Réunion (La Réunion)",
39425         "re",
39426         "262",
39427         0
39428       ],
39429       [
39430         "Romania (România)",
39431         "ro",
39432         "40"
39433       ],
39434       [
39435         "Russia (Россия)",
39436         "ru",
39437         "7",
39438         0
39439       ],
39440       [
39441         "Rwanda",
39442         "rw",
39443         "250"
39444       ],
39445       [
39446         "Saint Barthélemy",
39447         "bl",
39448         "590",
39449         1
39450       ],
39451       [
39452         "Saint Helena",
39453         "sh",
39454         "290"
39455       ],
39456       [
39457         "Saint Kitts and Nevis",
39458         "kn",
39459         "1869"
39460       ],
39461       [
39462         "Saint Lucia",
39463         "lc",
39464         "1758"
39465       ],
39466       [
39467         "Saint Martin (Saint-Martin (partie française))",
39468         "mf",
39469         "590",
39470         2
39471       ],
39472       [
39473         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39474         "pm",
39475         "508"
39476       ],
39477       [
39478         "Saint Vincent and the Grenadines",
39479         "vc",
39480         "1784"
39481       ],
39482       [
39483         "Samoa",
39484         "ws",
39485         "685"
39486       ],
39487       [
39488         "San Marino",
39489         "sm",
39490         "378"
39491       ],
39492       [
39493         "São Tomé and Príncipe (São Tomé e Príncipe)",
39494         "st",
39495         "239"
39496       ],
39497       [
39498         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39499         "sa",
39500         "966"
39501       ],
39502       [
39503         "Senegal (Sénégal)",
39504         "sn",
39505         "221"
39506       ],
39507       [
39508         "Serbia (Србија)",
39509         "rs",
39510         "381"
39511       ],
39512       [
39513         "Seychelles",
39514         "sc",
39515         "248"
39516       ],
39517       [
39518         "Sierra Leone",
39519         "sl",
39520         "232"
39521       ],
39522       [
39523         "Singapore",
39524         "sg",
39525         "65"
39526       ],
39527       [
39528         "Sint Maarten",
39529         "sx",
39530         "1721"
39531       ],
39532       [
39533         "Slovakia (Slovensko)",
39534         "sk",
39535         "421"
39536       ],
39537       [
39538         "Slovenia (Slovenija)",
39539         "si",
39540         "386"
39541       ],
39542       [
39543         "Solomon Islands",
39544         "sb",
39545         "677"
39546       ],
39547       [
39548         "Somalia (Soomaaliya)",
39549         "so",
39550         "252"
39551       ],
39552       [
39553         "South Africa",
39554         "za",
39555         "27"
39556       ],
39557       [
39558         "South Korea (대한민국)",
39559         "kr",
39560         "82"
39561       ],
39562       [
39563         "South Sudan (‫جنوب السودان‬‎)",
39564         "ss",
39565         "211"
39566       ],
39567       [
39568         "Spain (España)",
39569         "es",
39570         "34"
39571       ],
39572       [
39573         "Sri Lanka (ශ්‍රී ලංකාව)",
39574         "lk",
39575         "94"
39576       ],
39577       [
39578         "Sudan (‫السودان‬‎)",
39579         "sd",
39580         "249"
39581       ],
39582       [
39583         "Suriname",
39584         "sr",
39585         "597"
39586       ],
39587       [
39588         "Svalbard and Jan Mayen",
39589         "sj",
39590         "47",
39591         1
39592       ],
39593       [
39594         "Swaziland",
39595         "sz",
39596         "268"
39597       ],
39598       [
39599         "Sweden (Sverige)",
39600         "se",
39601         "46"
39602       ],
39603       [
39604         "Switzerland (Schweiz)",
39605         "ch",
39606         "41"
39607       ],
39608       [
39609         "Syria (‫سوريا‬‎)",
39610         "sy",
39611         "963"
39612       ],
39613       [
39614         "Taiwan (台灣)",
39615         "tw",
39616         "886"
39617       ],
39618       [
39619         "Tajikistan",
39620         "tj",
39621         "992"
39622       ],
39623       [
39624         "Tanzania",
39625         "tz",
39626         "255"
39627       ],
39628       [
39629         "Thailand (ไทย)",
39630         "th",
39631         "66"
39632       ],
39633       [
39634         "Timor-Leste",
39635         "tl",
39636         "670"
39637       ],
39638       [
39639         "Togo",
39640         "tg",
39641         "228"
39642       ],
39643       [
39644         "Tokelau",
39645         "tk",
39646         "690"
39647       ],
39648       [
39649         "Tonga",
39650         "to",
39651         "676"
39652       ],
39653       [
39654         "Trinidad and Tobago",
39655         "tt",
39656         "1868"
39657       ],
39658       [
39659         "Tunisia (‫تونس‬‎)",
39660         "tn",
39661         "216"
39662       ],
39663       [
39664         "Turkey (Türkiye)",
39665         "tr",
39666         "90"
39667       ],
39668       [
39669         "Turkmenistan",
39670         "tm",
39671         "993"
39672       ],
39673       [
39674         "Turks and Caicos Islands",
39675         "tc",
39676         "1649"
39677       ],
39678       [
39679         "Tuvalu",
39680         "tv",
39681         "688"
39682       ],
39683       [
39684         "U.S. Virgin Islands",
39685         "vi",
39686         "1340"
39687       ],
39688       [
39689         "Uganda",
39690         "ug",
39691         "256"
39692       ],
39693       [
39694         "Ukraine (Україна)",
39695         "ua",
39696         "380"
39697       ],
39698       [
39699         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39700         "ae",
39701         "971"
39702       ],
39703       [
39704         "United Kingdom",
39705         "gb",
39706         "44",
39707         0
39708       ],
39709       [
39710         "United States",
39711         "us",
39712         "1",
39713         0
39714       ],
39715       [
39716         "Uruguay",
39717         "uy",
39718         "598"
39719       ],
39720       [
39721         "Uzbekistan (Oʻzbekiston)",
39722         "uz",
39723         "998"
39724       ],
39725       [
39726         "Vanuatu",
39727         "vu",
39728         "678"
39729       ],
39730       [
39731         "Vatican City (Città del Vaticano)",
39732         "va",
39733         "39",
39734         1
39735       ],
39736       [
39737         "Venezuela",
39738         "ve",
39739         "58"
39740       ],
39741       [
39742         "Vietnam (Việt Nam)",
39743         "vn",
39744         "84"
39745       ],
39746       [
39747         "Wallis and Futuna (Wallis-et-Futuna)",
39748         "wf",
39749         "681"
39750       ],
39751       [
39752         "Western Sahara (‫الصحراء الغربية‬‎)",
39753         "eh",
39754         "212",
39755         1
39756       ],
39757       [
39758         "Yemen (‫اليمن‬‎)",
39759         "ye",
39760         "967"
39761       ],
39762       [
39763         "Zambia",
39764         "zm",
39765         "260"
39766       ],
39767       [
39768         "Zimbabwe",
39769         "zw",
39770         "263"
39771       ],
39772       [
39773         "Åland Islands",
39774         "ax",
39775         "358",
39776         1
39777       ]
39778   ];
39779   
39780   return d;
39781 }/**
39782 *    This script refer to:
39783 *    Title: International Telephone Input
39784 *    Author: Jack O'Connor
39785 *    Code version:  v12.1.12
39786 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39787 **/
39788
39789 /**
39790  * @class Roo.bootstrap.PhoneInput
39791  * @extends Roo.bootstrap.TriggerField
39792  * An input with International dial-code selection
39793  
39794  * @cfg {String} defaultDialCode default '+852'
39795  * @cfg {Array} preferedCountries default []
39796   
39797  * @constructor
39798  * Create a new PhoneInput.
39799  * @param {Object} config Configuration options
39800  */
39801
39802 Roo.bootstrap.PhoneInput = function(config) {
39803     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39804 };
39805
39806 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39807         
39808         listWidth: undefined,
39809         
39810         selectedClass: 'active',
39811         
39812         invalidClass : "has-warning",
39813         
39814         validClass: 'has-success',
39815         
39816         allowed: '0123456789',
39817         
39818         /**
39819          * @cfg {String} defaultDialCode The default dial code when initializing the input
39820          */
39821         defaultDialCode: '+852',
39822         
39823         /**
39824          * @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
39825          */
39826         preferedCountries: false,
39827         
39828         getAutoCreate : function()
39829         {
39830             var data = Roo.bootstrap.PhoneInputData();
39831             var align = this.labelAlign || this.parentLabelAlign();
39832             var id = Roo.id();
39833             
39834             this.allCountries = [];
39835             this.dialCodeMapping = [];
39836             
39837             for (var i = 0; i < data.length; i++) {
39838               var c = data[i];
39839               this.allCountries[i] = {
39840                 name: c[0],
39841                 iso2: c[1],
39842                 dialCode: c[2],
39843                 priority: c[3] || 0,
39844                 areaCodes: c[4] || null
39845               };
39846               this.dialCodeMapping[c[2]] = {
39847                   name: c[0],
39848                   iso2: c[1],
39849                   priority: c[3] || 0,
39850                   areaCodes: c[4] || null
39851               };
39852             }
39853             
39854             var cfg = {
39855                 cls: 'form-group',
39856                 cn: []
39857             };
39858             
39859             var input =  {
39860                 tag: 'input',
39861                 id : id,
39862                 cls : 'form-control tel-input',
39863                 autocomplete: 'new-password'
39864             };
39865             
39866             var hiddenInput = {
39867                 tag: 'input',
39868                 type: 'hidden',
39869                 cls: 'hidden-tel-input'
39870             };
39871             
39872             if (this.name) {
39873                 hiddenInput.name = this.name;
39874             }
39875             
39876             if (this.disabled) {
39877                 input.disabled = true;
39878             }
39879             
39880             var flag_container = {
39881                 tag: 'div',
39882                 cls: 'flag-box',
39883                 cn: [
39884                     {
39885                         tag: 'div',
39886                         cls: 'flag'
39887                     },
39888                     {
39889                         tag: 'div',
39890                         cls: 'caret'
39891                     }
39892                 ]
39893             };
39894             
39895             var box = {
39896                 tag: 'div',
39897                 cls: this.hasFeedback ? 'has-feedback' : '',
39898                 cn: [
39899                     hiddenInput,
39900                     input,
39901                     {
39902                         tag: 'input',
39903                         cls: 'dial-code-holder',
39904                         disabled: true
39905                     }
39906                 ]
39907             };
39908             
39909             var container = {
39910                 cls: 'roo-select2-container input-group',
39911                 cn: [
39912                     flag_container,
39913                     box
39914                 ]
39915             };
39916             
39917             if (this.fieldLabel.length) {
39918                 var indicator = {
39919                     tag: 'i',
39920                     tooltip: 'This field is required'
39921                 };
39922                 
39923                 var label = {
39924                     tag: 'label',
39925                     'for':  id,
39926                     cls: 'control-label',
39927                     cn: []
39928                 };
39929                 
39930                 var label_text = {
39931                     tag: 'span',
39932                     html: this.fieldLabel
39933                 };
39934                 
39935                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39936                 label.cn = [
39937                     indicator,
39938                     label_text
39939                 ];
39940                 
39941                 if(this.indicatorpos == 'right') {
39942                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39943                     label.cn = [
39944                         label_text,
39945                         indicator
39946                     ];
39947                 }
39948                 
39949                 if(align == 'left') {
39950                     container = {
39951                         tag: 'div',
39952                         cn: [
39953                             container
39954                         ]
39955                     };
39956                     
39957                     if(this.labelWidth > 12){
39958                         label.style = "width: " + this.labelWidth + 'px';
39959                     }
39960                     if(this.labelWidth < 13 && this.labelmd == 0){
39961                         this.labelmd = this.labelWidth;
39962                     }
39963                     if(this.labellg > 0){
39964                         label.cls += ' col-lg-' + this.labellg;
39965                         input.cls += ' col-lg-' + (12 - this.labellg);
39966                     }
39967                     if(this.labelmd > 0){
39968                         label.cls += ' col-md-' + this.labelmd;
39969                         container.cls += ' col-md-' + (12 - this.labelmd);
39970                     }
39971                     if(this.labelsm > 0){
39972                         label.cls += ' col-sm-' + this.labelsm;
39973                         container.cls += ' col-sm-' + (12 - this.labelsm);
39974                     }
39975                     if(this.labelxs > 0){
39976                         label.cls += ' col-xs-' + this.labelxs;
39977                         container.cls += ' col-xs-' + (12 - this.labelxs);
39978                     }
39979                 }
39980             }
39981             
39982             cfg.cn = [
39983                 label,
39984                 container
39985             ];
39986             
39987             var settings = this;
39988             
39989             ['xs','sm','md','lg'].map(function(size){
39990                 if (settings[size]) {
39991                     cfg.cls += ' col-' + size + '-' + settings[size];
39992                 }
39993             });
39994             
39995             this.store = new Roo.data.Store({
39996                 proxy : new Roo.data.MemoryProxy({}),
39997                 reader : new Roo.data.JsonReader({
39998                     fields : [
39999                         {
40000                             'name' : 'name',
40001                             'type' : 'string'
40002                         },
40003                         {
40004                             'name' : 'iso2',
40005                             'type' : 'string'
40006                         },
40007                         {
40008                             'name' : 'dialCode',
40009                             'type' : 'string'
40010                         },
40011                         {
40012                             'name' : 'priority',
40013                             'type' : 'string'
40014                         },
40015                         {
40016                             'name' : 'areaCodes',
40017                             'type' : 'string'
40018                         }
40019                     ]
40020                 })
40021             });
40022             
40023             if(!this.preferedCountries) {
40024                 this.preferedCountries = [
40025                     'hk',
40026                     'gb',
40027                     'us'
40028                 ];
40029             }
40030             
40031             var p = this.preferedCountries.reverse();
40032             
40033             if(p) {
40034                 for (var i = 0; i < p.length; i++) {
40035                     for (var j = 0; j < this.allCountries.length; j++) {
40036                         if(this.allCountries[j].iso2 == p[i]) {
40037                             var t = this.allCountries[j];
40038                             this.allCountries.splice(j,1);
40039                             this.allCountries.unshift(t);
40040                         }
40041                     } 
40042                 }
40043             }
40044             
40045             this.store.proxy.data = {
40046                 success: true,
40047                 data: this.allCountries
40048             };
40049             
40050             return cfg;
40051         },
40052         
40053         initEvents : function()
40054         {
40055             this.createList();
40056             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40057             
40058             this.indicator = this.indicatorEl();
40059             this.flag = this.flagEl();
40060             this.dialCodeHolder = this.dialCodeHolderEl();
40061             
40062             this.trigger = this.el.select('div.flag-box',true).first();
40063             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40064             
40065             var _this = this;
40066             
40067             (function(){
40068                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40069                 _this.list.setWidth(lw);
40070             }).defer(100);
40071             
40072             this.list.on('mouseover', this.onViewOver, this);
40073             this.list.on('mousemove', this.onViewMove, this);
40074             this.inputEl().on("keyup", this.onKeyUp, this);
40075             
40076             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40077
40078             this.view = new Roo.View(this.list, this.tpl, {
40079                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40080             });
40081             
40082             this.view.on('click', this.onViewClick, this);
40083             this.setValue(this.defaultDialCode);
40084         },
40085         
40086         onTriggerClick : function(e)
40087         {
40088             Roo.log('trigger click');
40089             if(this.disabled){
40090                 return;
40091             }
40092             
40093             if(this.isExpanded()){
40094                 this.collapse();
40095                 this.hasFocus = false;
40096             }else {
40097                 this.store.load({});
40098                 this.hasFocus = true;
40099                 this.expand();
40100             }
40101         },
40102         
40103         isExpanded : function()
40104         {
40105             return this.list.isVisible();
40106         },
40107         
40108         collapse : function()
40109         {
40110             if(!this.isExpanded()){
40111                 return;
40112             }
40113             this.list.hide();
40114             Roo.get(document).un('mousedown', this.collapseIf, this);
40115             Roo.get(document).un('mousewheel', this.collapseIf, this);
40116             this.fireEvent('collapse', this);
40117             this.validate();
40118         },
40119         
40120         expand : function()
40121         {
40122             Roo.log('expand');
40123
40124             if(this.isExpanded() || !this.hasFocus){
40125                 return;
40126             }
40127             
40128             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40129             this.list.setWidth(lw);
40130             
40131             this.list.show();
40132             this.restrictHeight();
40133             
40134             Roo.get(document).on('mousedown', this.collapseIf, this);
40135             Roo.get(document).on('mousewheel', this.collapseIf, this);
40136             
40137             this.fireEvent('expand', this);
40138         },
40139         
40140         restrictHeight : function()
40141         {
40142             this.list.alignTo(this.inputEl(), this.listAlign);
40143             this.list.alignTo(this.inputEl(), this.listAlign);
40144         },
40145         
40146         onViewOver : function(e, t)
40147         {
40148             if(this.inKeyMode){
40149                 return;
40150             }
40151             var item = this.view.findItemFromChild(t);
40152             
40153             if(item){
40154                 var index = this.view.indexOf(item);
40155                 this.select(index, false);
40156             }
40157         },
40158
40159         // private
40160         onViewClick : function(view, doFocus, el, e)
40161         {
40162             var index = this.view.getSelectedIndexes()[0];
40163             
40164             var r = this.store.getAt(index);
40165             
40166             if(r){
40167                 this.onSelect(r, index);
40168             }
40169             if(doFocus !== false && !this.blockFocus){
40170                 this.inputEl().focus();
40171             }
40172         },
40173         
40174         onViewMove : function(e, t)
40175         {
40176             this.inKeyMode = false;
40177         },
40178         
40179         select : function(index, scrollIntoView)
40180         {
40181             this.selectedIndex = index;
40182             this.view.select(index);
40183             if(scrollIntoView !== false){
40184                 var el = this.view.getNode(index);
40185                 if(el){
40186                     this.list.scrollChildIntoView(el, false);
40187                 }
40188             }
40189         },
40190         
40191         createList : function()
40192         {
40193             this.list = Roo.get(document.body).createChild({
40194                 tag: 'ul',
40195                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40196                 style: 'display:none'
40197             });
40198             
40199             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40200         },
40201         
40202         collapseIf : function(e)
40203         {
40204             var in_combo  = e.within(this.el);
40205             var in_list =  e.within(this.list);
40206             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40207             
40208             if (in_combo || in_list || is_list) {
40209                 return;
40210             }
40211             this.collapse();
40212         },
40213         
40214         onSelect : function(record, index)
40215         {
40216             if(this.fireEvent('beforeselect', this, record, index) !== false){
40217                 
40218                 this.setFlagClass(record.data.iso2);
40219                 this.setDialCode(record.data.dialCode);
40220                 this.hasFocus = false;
40221                 this.collapse();
40222                 this.fireEvent('select', this, record, index);
40223             }
40224         },
40225         
40226         flagEl : function()
40227         {
40228             var flag = this.el.select('div.flag',true).first();
40229             if(!flag){
40230                 return false;
40231             }
40232             return flag;
40233         },
40234         
40235         dialCodeHolderEl : function()
40236         {
40237             var d = this.el.select('input.dial-code-holder',true).first();
40238             if(!d){
40239                 return false;
40240             }
40241             return d;
40242         },
40243         
40244         setDialCode : function(v)
40245         {
40246             this.dialCodeHolder.dom.value = '+'+v;
40247         },
40248         
40249         setFlagClass : function(n)
40250         {
40251             this.flag.dom.className = 'flag '+n;
40252         },
40253         
40254         getValue : function()
40255         {
40256             var v = this.inputEl().getValue();
40257             if(this.dialCodeHolder) {
40258                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40259             }
40260             return v;
40261         },
40262         
40263         setValue : function(v)
40264         {
40265             var d = this.getDialCode(v);
40266             
40267             //invalid dial code
40268             if(v.length == 0 || !d || d.length == 0) {
40269                 if(this.rendered){
40270                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40271                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40272                 }
40273                 return;
40274             }
40275             
40276             //valid dial code
40277             this.setFlagClass(this.dialCodeMapping[d].iso2);
40278             this.setDialCode(d);
40279             this.inputEl().dom.value = v.replace('+'+d,'');
40280             this.hiddenEl().dom.value = this.getValue();
40281             
40282             this.validate();
40283         },
40284         
40285         getDialCode : function(v)
40286         {
40287             v = v ||  '';
40288             
40289             if (v.length == 0) {
40290                 return this.dialCodeHolder.dom.value;
40291             }
40292             
40293             var dialCode = "";
40294             if (v.charAt(0) != "+") {
40295                 return false;
40296             }
40297             var numericChars = "";
40298             for (var i = 1; i < v.length; i++) {
40299               var c = v.charAt(i);
40300               if (!isNaN(c)) {
40301                 numericChars += c;
40302                 if (this.dialCodeMapping[numericChars]) {
40303                   dialCode = v.substr(1, i);
40304                 }
40305                 if (numericChars.length == 4) {
40306                   break;
40307                 }
40308               }
40309             }
40310             return dialCode;
40311         },
40312         
40313         reset : function()
40314         {
40315             this.setValue(this.defaultDialCode);
40316             this.validate();
40317         },
40318         
40319         hiddenEl : function()
40320         {
40321             return this.el.select('input.hidden-tel-input',true).first();
40322         },
40323         
40324         onKeyUp : function(e){
40325             
40326             var k = e.getKey();
40327             var c = e.getCharCode();
40328             
40329             if(
40330                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40331                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40332             ){
40333                 e.stopEvent();
40334             }
40335             
40336             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40337             //     return;
40338             // }
40339             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40340                 e.stopEvent();
40341             }
40342             
40343             this.setValue(this.getValue());
40344         }
40345         
40346 });
40347 /**
40348  * @class Roo.bootstrap.MoneyField
40349  * @extends Roo.bootstrap.ComboBox
40350  * Bootstrap MoneyField class
40351  * 
40352  * @constructor
40353  * Create a new MoneyField.
40354  * @param {Object} config Configuration options
40355  */
40356
40357 Roo.bootstrap.MoneyField = function(config) {
40358     
40359     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40360     
40361 };
40362
40363 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40364     
40365     /**
40366      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40367      */
40368     allowDecimals : true,
40369     /**
40370      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40371      */
40372     decimalSeparator : ".",
40373     /**
40374      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40375      */
40376     decimalPrecision : 0,
40377     /**
40378      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40379      */
40380     allowNegative : true,
40381     /**
40382      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40383      */
40384     allowZero: true,
40385     /**
40386      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40387      */
40388     minValue : Number.NEGATIVE_INFINITY,
40389     /**
40390      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40391      */
40392     maxValue : Number.MAX_VALUE,
40393     /**
40394      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40395      */
40396     minText : "The minimum value for this field is {0}",
40397     /**
40398      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40399      */
40400     maxText : "The maximum value for this field is {0}",
40401     /**
40402      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40403      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40404      */
40405     nanText : "{0} is not a valid number",
40406     /**
40407      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40408      */
40409     castInt : true,
40410     /**
40411      * @cfg {String} defaults currency of the MoneyField
40412      * value should be in lkey
40413      */
40414     defaultCurrency : false,
40415     /**
40416      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40417      */
40418     thousandsDelimiter : false,
40419     
40420     
40421     inputlg : 9,
40422     inputmd : 9,
40423     inputsm : 9,
40424     inputxs : 6,
40425     
40426     store : false,
40427     
40428     getAutoCreate : function()
40429     {
40430         var align = this.labelAlign || this.parentLabelAlign();
40431         
40432         var id = Roo.id();
40433
40434         var cfg = {
40435             cls: 'form-group',
40436             cn: []
40437         };
40438
40439         var input =  {
40440             tag: 'input',
40441             id : id,
40442             cls : 'form-control roo-money-amount-input',
40443             autocomplete: 'new-password'
40444         };
40445         
40446         var hiddenInput = {
40447             tag: 'input',
40448             type: 'hidden',
40449             id: Roo.id(),
40450             cls: 'hidden-number-input'
40451         };
40452         
40453         if (this.name) {
40454             hiddenInput.name = this.name;
40455         }
40456
40457         if (this.disabled) {
40458             input.disabled = true;
40459         }
40460
40461         var clg = 12 - this.inputlg;
40462         var cmd = 12 - this.inputmd;
40463         var csm = 12 - this.inputsm;
40464         var cxs = 12 - this.inputxs;
40465         
40466         var container = {
40467             tag : 'div',
40468             cls : 'row roo-money-field',
40469             cn : [
40470                 {
40471                     tag : 'div',
40472                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40473                     cn : [
40474                         {
40475                             tag : 'div',
40476                             cls: 'roo-select2-container input-group',
40477                             cn: [
40478                                 {
40479                                     tag : 'input',
40480                                     cls : 'form-control roo-money-currency-input',
40481                                     autocomplete: 'new-password',
40482                                     readOnly : 1,
40483                                     name : this.currencyName
40484                                 },
40485                                 {
40486                                     tag :'span',
40487                                     cls : 'input-group-addon',
40488                                     cn : [
40489                                         {
40490                                             tag: 'span',
40491                                             cls: 'caret'
40492                                         }
40493                                     ]
40494                                 }
40495                             ]
40496                         }
40497                     ]
40498                 },
40499                 {
40500                     tag : 'div',
40501                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40502                     cn : [
40503                         {
40504                             tag: 'div',
40505                             cls: this.hasFeedback ? 'has-feedback' : '',
40506                             cn: [
40507                                 input
40508                             ]
40509                         }
40510                     ]
40511                 }
40512             ]
40513             
40514         };
40515         
40516         if (this.fieldLabel.length) {
40517             var indicator = {
40518                 tag: 'i',
40519                 tooltip: 'This field is required'
40520             };
40521
40522             var label = {
40523                 tag: 'label',
40524                 'for':  id,
40525                 cls: 'control-label',
40526                 cn: []
40527             };
40528
40529             var label_text = {
40530                 tag: 'span',
40531                 html: this.fieldLabel
40532             };
40533
40534             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40535             label.cn = [
40536                 indicator,
40537                 label_text
40538             ];
40539
40540             if(this.indicatorpos == 'right') {
40541                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40542                 label.cn = [
40543                     label_text,
40544                     indicator
40545                 ];
40546             }
40547
40548             if(align == 'left') {
40549                 container = {
40550                     tag: 'div',
40551                     cn: [
40552                         container
40553                     ]
40554                 };
40555
40556                 if(this.labelWidth > 12){
40557                     label.style = "width: " + this.labelWidth + 'px';
40558                 }
40559                 if(this.labelWidth < 13 && this.labelmd == 0){
40560                     this.labelmd = this.labelWidth;
40561                 }
40562                 if(this.labellg > 0){
40563                     label.cls += ' col-lg-' + this.labellg;
40564                     input.cls += ' col-lg-' + (12 - this.labellg);
40565                 }
40566                 if(this.labelmd > 0){
40567                     label.cls += ' col-md-' + this.labelmd;
40568                     container.cls += ' col-md-' + (12 - this.labelmd);
40569                 }
40570                 if(this.labelsm > 0){
40571                     label.cls += ' col-sm-' + this.labelsm;
40572                     container.cls += ' col-sm-' + (12 - this.labelsm);
40573                 }
40574                 if(this.labelxs > 0){
40575                     label.cls += ' col-xs-' + this.labelxs;
40576                     container.cls += ' col-xs-' + (12 - this.labelxs);
40577                 }
40578             }
40579         }
40580
40581         cfg.cn = [
40582             label,
40583             container,
40584             hiddenInput
40585         ];
40586         
40587         var settings = this;
40588
40589         ['xs','sm','md','lg'].map(function(size){
40590             if (settings[size]) {
40591                 cfg.cls += ' col-' + size + '-' + settings[size];
40592             }
40593         });
40594         
40595         return cfg;
40596     },
40597     
40598     initEvents : function()
40599     {
40600         this.indicator = this.indicatorEl();
40601         
40602         this.initCurrencyEvent();
40603         
40604         this.initNumberEvent();
40605     },
40606     
40607     initCurrencyEvent : function()
40608     {
40609         if (!this.store) {
40610             throw "can not find store for combo";
40611         }
40612         
40613         this.store = Roo.factory(this.store, Roo.data);
40614         this.store.parent = this;
40615         
40616         this.createList();
40617         
40618         this.triggerEl = this.el.select('.input-group-addon', true).first();
40619         
40620         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40621         
40622         var _this = this;
40623         
40624         (function(){
40625             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40626             _this.list.setWidth(lw);
40627         }).defer(100);
40628         
40629         this.list.on('mouseover', this.onViewOver, this);
40630         this.list.on('mousemove', this.onViewMove, this);
40631         this.list.on('scroll', this.onViewScroll, this);
40632         
40633         if(!this.tpl){
40634             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40635         }
40636         
40637         this.view = new Roo.View(this.list, this.tpl, {
40638             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40639         });
40640         
40641         this.view.on('click', this.onViewClick, this);
40642         
40643         this.store.on('beforeload', this.onBeforeLoad, this);
40644         this.store.on('load', this.onLoad, this);
40645         this.store.on('loadexception', this.onLoadException, this);
40646         
40647         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40648             "up" : function(e){
40649                 this.inKeyMode = true;
40650                 this.selectPrev();
40651             },
40652
40653             "down" : function(e){
40654                 if(!this.isExpanded()){
40655                     this.onTriggerClick();
40656                 }else{
40657                     this.inKeyMode = true;
40658                     this.selectNext();
40659                 }
40660             },
40661
40662             "enter" : function(e){
40663                 this.collapse();
40664                 
40665                 if(this.fireEvent("specialkey", this, e)){
40666                     this.onViewClick(false);
40667                 }
40668                 
40669                 return true;
40670             },
40671
40672             "esc" : function(e){
40673                 this.collapse();
40674             },
40675
40676             "tab" : function(e){
40677                 this.collapse();
40678                 
40679                 if(this.fireEvent("specialkey", this, e)){
40680                     this.onViewClick(false);
40681                 }
40682                 
40683                 return true;
40684             },
40685
40686             scope : this,
40687
40688             doRelay : function(foo, bar, hname){
40689                 if(hname == 'down' || this.scope.isExpanded()){
40690                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40691                 }
40692                 return true;
40693             },
40694
40695             forceKeyDown: true
40696         });
40697         
40698         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40699         
40700     },
40701     
40702     initNumberEvent : function(e)
40703     {
40704         this.inputEl().on("keydown" , this.fireKey,  this);
40705         this.inputEl().on("focus", this.onFocus,  this);
40706         this.inputEl().on("blur", this.onBlur,  this);
40707         
40708         this.inputEl().relayEvent('keyup', this);
40709         
40710         if(this.indicator){
40711             this.indicator.addClass('invisible');
40712         }
40713  
40714         this.originalValue = this.getValue();
40715         
40716         if(this.validationEvent == 'keyup'){
40717             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40718             this.inputEl().on('keyup', this.filterValidation, this);
40719         }
40720         else if(this.validationEvent !== false){
40721             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40722         }
40723         
40724         if(this.selectOnFocus){
40725             this.on("focus", this.preFocus, this);
40726             
40727         }
40728         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40729             this.inputEl().on("keypress", this.filterKeys, this);
40730         } else {
40731             this.inputEl().relayEvent('keypress', this);
40732         }
40733         
40734         var allowed = "0123456789";
40735         
40736         if(this.allowDecimals){
40737             allowed += this.decimalSeparator;
40738         }
40739         
40740         if(this.allowNegative){
40741             allowed += "-";
40742         }
40743         
40744         if(this.thousandsDelimiter) {
40745             allowed += ",";
40746         }
40747         
40748         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40749         
40750         var keyPress = function(e){
40751             
40752             var k = e.getKey();
40753             
40754             var c = e.getCharCode();
40755             
40756             if(
40757                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40758                     allowed.indexOf(String.fromCharCode(c)) === -1
40759             ){
40760                 e.stopEvent();
40761                 return;
40762             }
40763             
40764             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40765                 return;
40766             }
40767             
40768             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40769                 e.stopEvent();
40770             }
40771         };
40772         
40773         this.inputEl().on("keypress", keyPress, this);
40774         
40775     },
40776     
40777     onTriggerClick : function(e)
40778     {   
40779         if(this.disabled){
40780             return;
40781         }
40782         
40783         this.page = 0;
40784         this.loadNext = false;
40785         
40786         if(this.isExpanded()){
40787             this.collapse();
40788             return;
40789         }
40790         
40791         this.hasFocus = true;
40792         
40793         if(this.triggerAction == 'all') {
40794             this.doQuery(this.allQuery, true);
40795             return;
40796         }
40797         
40798         this.doQuery(this.getRawValue());
40799     },
40800     
40801     getCurrency : function()
40802     {   
40803         var v = this.currencyEl().getValue();
40804         
40805         return v;
40806     },
40807     
40808     restrictHeight : function()
40809     {
40810         this.list.alignTo(this.currencyEl(), this.listAlign);
40811         this.list.alignTo(this.currencyEl(), this.listAlign);
40812     },
40813     
40814     onViewClick : function(view, doFocus, el, e)
40815     {
40816         var index = this.view.getSelectedIndexes()[0];
40817         
40818         var r = this.store.getAt(index);
40819         
40820         if(r){
40821             this.onSelect(r, index);
40822         }
40823     },
40824     
40825     onSelect : function(record, index){
40826         
40827         if(this.fireEvent('beforeselect', this, record, index) !== false){
40828         
40829             this.setFromCurrencyData(index > -1 ? record.data : false);
40830             
40831             this.collapse();
40832             
40833             this.fireEvent('select', this, record, index);
40834         }
40835     },
40836     
40837     setFromCurrencyData : function(o)
40838     {
40839         var currency = '';
40840         
40841         this.lastCurrency = o;
40842         
40843         if (this.currencyField) {
40844             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40845         } else {
40846             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40847         }
40848         
40849         this.lastSelectionText = currency;
40850         
40851         //setting default currency
40852         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40853             this.setCurrency(this.defaultCurrency);
40854             return;
40855         }
40856         
40857         this.setCurrency(currency);
40858     },
40859     
40860     setFromData : function(o)
40861     {
40862         var c = {};
40863         
40864         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40865         
40866         this.setFromCurrencyData(c);
40867         
40868         var value = '';
40869         
40870         if (this.name) {
40871             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40872         } else {
40873             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40874         }
40875         
40876         this.setValue(value);
40877         
40878     },
40879     
40880     setCurrency : function(v)
40881     {   
40882         this.currencyValue = v;
40883         
40884         if(this.rendered){
40885             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40886             this.validate();
40887         }
40888     },
40889     
40890     setValue : function(v)
40891     {
40892         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40893         
40894         this.value = v;
40895         
40896         if(this.rendered){
40897             
40898             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40899             
40900             this.inputEl().dom.value = (v == '') ? '' :
40901                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40902             
40903             if(!this.allowZero && v === '0') {
40904                 this.hiddenEl().dom.value = '';
40905                 this.inputEl().dom.value = '';
40906             }
40907             
40908             this.validate();
40909         }
40910     },
40911     
40912     getRawValue : function()
40913     {
40914         var v = this.inputEl().getValue();
40915         
40916         return v;
40917     },
40918     
40919     getValue : function()
40920     {
40921         return this.fixPrecision(this.parseValue(this.getRawValue()));
40922     },
40923     
40924     parseValue : function(value)
40925     {
40926         if(this.thousandsDelimiter) {
40927             value += "";
40928             r = new RegExp(",", "g");
40929             value = value.replace(r, "");
40930         }
40931         
40932         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40933         return isNaN(value) ? '' : value;
40934         
40935     },
40936     
40937     fixPrecision : function(value)
40938     {
40939         if(this.thousandsDelimiter) {
40940             value += "";
40941             r = new RegExp(",", "g");
40942             value = value.replace(r, "");
40943         }
40944         
40945         var nan = isNaN(value);
40946         
40947         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40948             return nan ? '' : value;
40949         }
40950         return parseFloat(value).toFixed(this.decimalPrecision);
40951     },
40952     
40953     decimalPrecisionFcn : function(v)
40954     {
40955         return Math.floor(v);
40956     },
40957     
40958     validateValue : function(value)
40959     {
40960         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40961             return false;
40962         }
40963         
40964         var num = this.parseValue(value);
40965         
40966         if(isNaN(num)){
40967             this.markInvalid(String.format(this.nanText, value));
40968             return false;
40969         }
40970         
40971         if(num < this.minValue){
40972             this.markInvalid(String.format(this.minText, this.minValue));
40973             return false;
40974         }
40975         
40976         if(num > this.maxValue){
40977             this.markInvalid(String.format(this.maxText, this.maxValue));
40978             return false;
40979         }
40980         
40981         return true;
40982     },
40983     
40984     validate : function()
40985     {
40986         if(this.disabled || this.allowBlank){
40987             this.markValid();
40988             return true;
40989         }
40990         
40991         var currency = this.getCurrency();
40992         
40993         if(this.validateValue(this.getRawValue()) && currency.length){
40994             this.markValid();
40995             return true;
40996         }
40997         
40998         this.markInvalid();
40999         return false;
41000     },
41001     
41002     getName: function()
41003     {
41004         return this.name;
41005     },
41006     
41007     beforeBlur : function()
41008     {
41009         if(!this.castInt){
41010             return;
41011         }
41012         
41013         var v = this.parseValue(this.getRawValue());
41014         
41015         if(v || v == 0){
41016             this.setValue(v);
41017         }
41018     },
41019     
41020     onBlur : function()
41021     {
41022         this.beforeBlur();
41023         
41024         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41025             //this.el.removeClass(this.focusClass);
41026         }
41027         
41028         this.hasFocus = false;
41029         
41030         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41031             this.validate();
41032         }
41033         
41034         var v = this.getValue();
41035         
41036         if(String(v) !== String(this.startValue)){
41037             this.fireEvent('change', this, v, this.startValue);
41038         }
41039         
41040         this.fireEvent("blur", this);
41041     },
41042     
41043     inputEl : function()
41044     {
41045         return this.el.select('.roo-money-amount-input', true).first();
41046     },
41047     
41048     currencyEl : function()
41049     {
41050         return this.el.select('.roo-money-currency-input', true).first();
41051     },
41052     
41053     hiddenEl : function()
41054     {
41055         return this.el.select('input.hidden-number-input',true).first();
41056     }
41057     
41058 });