sync
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     
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         Roo.log(this.CSS);
7093         
7094         this.CSS.updateRule(thSelector, "display", "");
7095         this.CSS.updateRule(tdSelector, "display", "");
7096         
7097         if(hidden){
7098             this.CSS.updateRule(thSelector, "display", "none");
7099             this.CSS.updateRule(tdSelector, "display", "none");
7100         }
7101         
7102         this.onHeaderChange();
7103         this.onLoad();
7104         
7105     }
7106     
7107 });
7108
7109  
7110
7111  /*
7112  * - LGPL
7113  *
7114  * table cell
7115  * 
7116  */
7117
7118 /**
7119  * @class Roo.bootstrap.TableCell
7120  * @extends Roo.bootstrap.Component
7121  * Bootstrap TableCell class
7122  * @cfg {String} html cell contain text
7123  * @cfg {String} cls cell class
7124  * @cfg {String} tag cell tag (td|th) default td
7125  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7126  * @cfg {String} align Aligns the content in a cell
7127  * @cfg {String} axis Categorizes cells
7128  * @cfg {String} bgcolor Specifies the background color of a cell
7129  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7130  * @cfg {Number} colspan Specifies the number of columns a cell should span
7131  * @cfg {String} headers Specifies one or more header cells a cell is related to
7132  * @cfg {Number} height Sets the height of a cell
7133  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7134  * @cfg {Number} rowspan Sets the number of rows a cell should span
7135  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7136  * @cfg {String} valign Vertical aligns the content in a cell
7137  * @cfg {Number} width Specifies the width of a cell
7138  * 
7139  * @constructor
7140  * Create a new TableCell
7141  * @param {Object} config The config object
7142  */
7143
7144 Roo.bootstrap.TableCell = function(config){
7145     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7146 };
7147
7148 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7149     
7150     html: false,
7151     cls: false,
7152     tag: false,
7153     abbr: false,
7154     align: false,
7155     axis: false,
7156     bgcolor: false,
7157     charoff: false,
7158     colspan: false,
7159     headers: false,
7160     height: false,
7161     nowrap: false,
7162     rowspan: false,
7163     scope: false,
7164     valign: false,
7165     width: false,
7166     
7167     
7168     getAutoCreate : function(){
7169         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7170         
7171         cfg = {
7172             tag: 'td'
7173         };
7174         
7175         if(this.tag){
7176             cfg.tag = this.tag;
7177         }
7178         
7179         if (this.html) {
7180             cfg.html=this.html
7181         }
7182         if (this.cls) {
7183             cfg.cls=this.cls
7184         }
7185         if (this.abbr) {
7186             cfg.abbr=this.abbr
7187         }
7188         if (this.align) {
7189             cfg.align=this.align
7190         }
7191         if (this.axis) {
7192             cfg.axis=this.axis
7193         }
7194         if (this.bgcolor) {
7195             cfg.bgcolor=this.bgcolor
7196         }
7197         if (this.charoff) {
7198             cfg.charoff=this.charoff
7199         }
7200         if (this.colspan) {
7201             cfg.colspan=this.colspan
7202         }
7203         if (this.headers) {
7204             cfg.headers=this.headers
7205         }
7206         if (this.height) {
7207             cfg.height=this.height
7208         }
7209         if (this.nowrap) {
7210             cfg.nowrap=this.nowrap
7211         }
7212         if (this.rowspan) {
7213             cfg.rowspan=this.rowspan
7214         }
7215         if (this.scope) {
7216             cfg.scope=this.scope
7217         }
7218         if (this.valign) {
7219             cfg.valign=this.valign
7220         }
7221         if (this.width) {
7222             cfg.width=this.width
7223         }
7224         
7225         
7226         return cfg;
7227     }
7228    
7229 });
7230
7231  
7232
7233  /*
7234  * - LGPL
7235  *
7236  * table row
7237  * 
7238  */
7239
7240 /**
7241  * @class Roo.bootstrap.TableRow
7242  * @extends Roo.bootstrap.Component
7243  * Bootstrap TableRow class
7244  * @cfg {String} cls row class
7245  * @cfg {String} align Aligns the content in a table row
7246  * @cfg {String} bgcolor Specifies a background color for a table row
7247  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7248  * @cfg {String} valign Vertical aligns the content in a table row
7249  * 
7250  * @constructor
7251  * Create a new TableRow
7252  * @param {Object} config The config object
7253  */
7254
7255 Roo.bootstrap.TableRow = function(config){
7256     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7257 };
7258
7259 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7260     
7261     cls: false,
7262     align: false,
7263     bgcolor: false,
7264     charoff: false,
7265     valign: false,
7266     
7267     getAutoCreate : function(){
7268         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7269         
7270         cfg = {
7271             tag: 'tr'
7272         };
7273             
7274         if(this.cls){
7275             cfg.cls = this.cls;
7276         }
7277         if(this.align){
7278             cfg.align = this.align;
7279         }
7280         if(this.bgcolor){
7281             cfg.bgcolor = this.bgcolor;
7282         }
7283         if(this.charoff){
7284             cfg.charoff = this.charoff;
7285         }
7286         if(this.valign){
7287             cfg.valign = this.valign;
7288         }
7289         
7290         return cfg;
7291     }
7292    
7293 });
7294
7295  
7296
7297  /*
7298  * - LGPL
7299  *
7300  * table body
7301  * 
7302  */
7303
7304 /**
7305  * @class Roo.bootstrap.TableBody
7306  * @extends Roo.bootstrap.Component
7307  * Bootstrap TableBody class
7308  * @cfg {String} cls element class
7309  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7310  * @cfg {String} align Aligns the content inside the element
7311  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7312  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7313  * 
7314  * @constructor
7315  * Create a new TableBody
7316  * @param {Object} config The config object
7317  */
7318
7319 Roo.bootstrap.TableBody = function(config){
7320     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7321 };
7322
7323 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7324     
7325     cls: false,
7326     tag: false,
7327     align: false,
7328     charoff: false,
7329     valign: false,
7330     
7331     getAutoCreate : function(){
7332         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7333         
7334         cfg = {
7335             tag: 'tbody'
7336         };
7337             
7338         if (this.cls) {
7339             cfg.cls=this.cls
7340         }
7341         if(this.tag){
7342             cfg.tag = this.tag;
7343         }
7344         
7345         if(this.align){
7346             cfg.align = this.align;
7347         }
7348         if(this.charoff){
7349             cfg.charoff = this.charoff;
7350         }
7351         if(this.valign){
7352             cfg.valign = this.valign;
7353         }
7354         
7355         return cfg;
7356     }
7357     
7358     
7359 //    initEvents : function()
7360 //    {
7361 //        
7362 //        if(!this.store){
7363 //            return;
7364 //        }
7365 //        
7366 //        this.store = Roo.factory(this.store, Roo.data);
7367 //        this.store.on('load', this.onLoad, this);
7368 //        
7369 //        this.store.load();
7370 //        
7371 //    },
7372 //    
7373 //    onLoad: function () 
7374 //    {   
7375 //        this.fireEvent('load', this);
7376 //    }
7377 //    
7378 //   
7379 });
7380
7381  
7382
7383  /*
7384  * Based on:
7385  * Ext JS Library 1.1.1
7386  * Copyright(c) 2006-2007, Ext JS, LLC.
7387  *
7388  * Originally Released Under LGPL - original licence link has changed is not relivant.
7389  *
7390  * Fork - LGPL
7391  * <script type="text/javascript">
7392  */
7393
7394 // as we use this in bootstrap.
7395 Roo.namespace('Roo.form');
7396  /**
7397  * @class Roo.form.Action
7398  * Internal Class used to handle form actions
7399  * @constructor
7400  * @param {Roo.form.BasicForm} el The form element or its id
7401  * @param {Object} config Configuration options
7402  */
7403
7404  
7405  
7406 // define the action interface
7407 Roo.form.Action = function(form, options){
7408     this.form = form;
7409     this.options = options || {};
7410 };
7411 /**
7412  * Client Validation Failed
7413  * @const 
7414  */
7415 Roo.form.Action.CLIENT_INVALID = 'client';
7416 /**
7417  * Server Validation Failed
7418  * @const 
7419  */
7420 Roo.form.Action.SERVER_INVALID = 'server';
7421  /**
7422  * Connect to Server Failed
7423  * @const 
7424  */
7425 Roo.form.Action.CONNECT_FAILURE = 'connect';
7426 /**
7427  * Reading Data from Server Failed
7428  * @const 
7429  */
7430 Roo.form.Action.LOAD_FAILURE = 'load';
7431
7432 Roo.form.Action.prototype = {
7433     type : 'default',
7434     failureType : undefined,
7435     response : undefined,
7436     result : undefined,
7437
7438     // interface method
7439     run : function(options){
7440
7441     },
7442
7443     // interface method
7444     success : function(response){
7445
7446     },
7447
7448     // interface method
7449     handleResponse : function(response){
7450
7451     },
7452
7453     // default connection failure
7454     failure : function(response){
7455         
7456         this.response = response;
7457         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7458         this.form.afterAction(this, false);
7459     },
7460
7461     processResponse : function(response){
7462         this.response = response;
7463         if(!response.responseText){
7464             return true;
7465         }
7466         this.result = this.handleResponse(response);
7467         return this.result;
7468     },
7469
7470     // utility functions used internally
7471     getUrl : function(appendParams){
7472         var url = this.options.url || this.form.url || this.form.el.dom.action;
7473         if(appendParams){
7474             var p = this.getParams();
7475             if(p){
7476                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7477             }
7478         }
7479         return url;
7480     },
7481
7482     getMethod : function(){
7483         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7484     },
7485
7486     getParams : function(){
7487         var bp = this.form.baseParams;
7488         var p = this.options.params;
7489         if(p){
7490             if(typeof p == "object"){
7491                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7492             }else if(typeof p == 'string' && bp){
7493                 p += '&' + Roo.urlEncode(bp);
7494             }
7495         }else if(bp){
7496             p = Roo.urlEncode(bp);
7497         }
7498         return p;
7499     },
7500
7501     createCallback : function(){
7502         return {
7503             success: this.success,
7504             failure: this.failure,
7505             scope: this,
7506             timeout: (this.form.timeout*1000),
7507             upload: this.form.fileUpload ? this.success : undefined
7508         };
7509     }
7510 };
7511
7512 Roo.form.Action.Submit = function(form, options){
7513     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7514 };
7515
7516 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7517     type : 'submit',
7518
7519     haveProgress : false,
7520     uploadComplete : false,
7521     
7522     // uploadProgress indicator.
7523     uploadProgress : function()
7524     {
7525         if (!this.form.progressUrl) {
7526             return;
7527         }
7528         
7529         if (!this.haveProgress) {
7530             Roo.MessageBox.progress("Uploading", "Uploading");
7531         }
7532         if (this.uploadComplete) {
7533            Roo.MessageBox.hide();
7534            return;
7535         }
7536         
7537         this.haveProgress = true;
7538    
7539         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7540         
7541         var c = new Roo.data.Connection();
7542         c.request({
7543             url : this.form.progressUrl,
7544             params: {
7545                 id : uid
7546             },
7547             method: 'GET',
7548             success : function(req){
7549                //console.log(data);
7550                 var rdata = false;
7551                 var edata;
7552                 try  {
7553                    rdata = Roo.decode(req.responseText)
7554                 } catch (e) {
7555                     Roo.log("Invalid data from server..");
7556                     Roo.log(edata);
7557                     return;
7558                 }
7559                 if (!rdata || !rdata.success) {
7560                     Roo.log(rdata);
7561                     Roo.MessageBox.alert(Roo.encode(rdata));
7562                     return;
7563                 }
7564                 var data = rdata.data;
7565                 
7566                 if (this.uploadComplete) {
7567                    Roo.MessageBox.hide();
7568                    return;
7569                 }
7570                    
7571                 if (data){
7572                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7573                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7574                     );
7575                 }
7576                 this.uploadProgress.defer(2000,this);
7577             },
7578        
7579             failure: function(data) {
7580                 Roo.log('progress url failed ');
7581                 Roo.log(data);
7582             },
7583             scope : this
7584         });
7585            
7586     },
7587     
7588     
7589     run : function()
7590     {
7591         // run get Values on the form, so it syncs any secondary forms.
7592         this.form.getValues();
7593         
7594         var o = this.options;
7595         var method = this.getMethod();
7596         var isPost = method == 'POST';
7597         if(o.clientValidation === false || this.form.isValid()){
7598             
7599             if (this.form.progressUrl) {
7600                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7601                     (new Date() * 1) + '' + Math.random());
7602                     
7603             } 
7604             
7605             
7606             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7607                 form:this.form.el.dom,
7608                 url:this.getUrl(!isPost),
7609                 method: method,
7610                 params:isPost ? this.getParams() : null,
7611                 isUpload: this.form.fileUpload
7612             }));
7613             
7614             this.uploadProgress();
7615
7616         }else if (o.clientValidation !== false){ // client validation failed
7617             this.failureType = Roo.form.Action.CLIENT_INVALID;
7618             this.form.afterAction(this, false);
7619         }
7620     },
7621
7622     success : function(response)
7623     {
7624         this.uploadComplete= true;
7625         if (this.haveProgress) {
7626             Roo.MessageBox.hide();
7627         }
7628         
7629         
7630         var result = this.processResponse(response);
7631         if(result === true || result.success){
7632             this.form.afterAction(this, true);
7633             return;
7634         }
7635         if(result.errors){
7636             this.form.markInvalid(result.errors);
7637             this.failureType = Roo.form.Action.SERVER_INVALID;
7638         }
7639         this.form.afterAction(this, false);
7640     },
7641     failure : function(response)
7642     {
7643         this.uploadComplete= true;
7644         if (this.haveProgress) {
7645             Roo.MessageBox.hide();
7646         }
7647         
7648         this.response = response;
7649         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7650         this.form.afterAction(this, false);
7651     },
7652     
7653     handleResponse : function(response){
7654         if(this.form.errorReader){
7655             var rs = this.form.errorReader.read(response);
7656             var errors = [];
7657             if(rs.records){
7658                 for(var i = 0, len = rs.records.length; i < len; i++) {
7659                     var r = rs.records[i];
7660                     errors[i] = r.data;
7661                 }
7662             }
7663             if(errors.length < 1){
7664                 errors = null;
7665             }
7666             return {
7667                 success : rs.success,
7668                 errors : errors
7669             };
7670         }
7671         var ret = false;
7672         try {
7673             ret = Roo.decode(response.responseText);
7674         } catch (e) {
7675             ret = {
7676                 success: false,
7677                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7678                 errors : []
7679             };
7680         }
7681         return ret;
7682         
7683     }
7684 });
7685
7686
7687 Roo.form.Action.Load = function(form, options){
7688     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7689     this.reader = this.form.reader;
7690 };
7691
7692 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7693     type : 'load',
7694
7695     run : function(){
7696         
7697         Roo.Ajax.request(Roo.apply(
7698                 this.createCallback(), {
7699                     method:this.getMethod(),
7700                     url:this.getUrl(false),
7701                     params:this.getParams()
7702         }));
7703     },
7704
7705     success : function(response){
7706         
7707         var result = this.processResponse(response);
7708         if(result === true || !result.success || !result.data){
7709             this.failureType = Roo.form.Action.LOAD_FAILURE;
7710             this.form.afterAction(this, false);
7711             return;
7712         }
7713         this.form.clearInvalid();
7714         this.form.setValues(result.data);
7715         this.form.afterAction(this, true);
7716     },
7717
7718     handleResponse : function(response){
7719         if(this.form.reader){
7720             var rs = this.form.reader.read(response);
7721             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7722             return {
7723                 success : rs.success,
7724                 data : data
7725             };
7726         }
7727         return Roo.decode(response.responseText);
7728     }
7729 });
7730
7731 Roo.form.Action.ACTION_TYPES = {
7732     'load' : Roo.form.Action.Load,
7733     'submit' : Roo.form.Action.Submit
7734 };/*
7735  * - LGPL
7736  *
7737  * form
7738  *
7739  */
7740
7741 /**
7742  * @class Roo.bootstrap.Form
7743  * @extends Roo.bootstrap.Component
7744  * Bootstrap Form class
7745  * @cfg {String} method  GET | POST (default POST)
7746  * @cfg {String} labelAlign top | left (default top)
7747  * @cfg {String} align left  | right - for navbars
7748  * @cfg {Boolean} loadMask load mask when submit (default true)
7749
7750  *
7751  * @constructor
7752  * Create a new Form
7753  * @param {Object} config The config object
7754  */
7755
7756
7757 Roo.bootstrap.Form = function(config){
7758     
7759     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7760     
7761     Roo.bootstrap.Form.popover.apply();
7762     
7763     this.addEvents({
7764         /**
7765          * @event clientvalidation
7766          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7767          * @param {Form} this
7768          * @param {Boolean} valid true if the form has passed client-side validation
7769          */
7770         clientvalidation: true,
7771         /**
7772          * @event beforeaction
7773          * Fires before any action is performed. Return false to cancel the action.
7774          * @param {Form} this
7775          * @param {Action} action The action to be performed
7776          */
7777         beforeaction: true,
7778         /**
7779          * @event actionfailed
7780          * Fires when an action fails.
7781          * @param {Form} this
7782          * @param {Action} action The action that failed
7783          */
7784         actionfailed : true,
7785         /**
7786          * @event actioncomplete
7787          * Fires when an action is completed.
7788          * @param {Form} this
7789          * @param {Action} action The action that completed
7790          */
7791         actioncomplete : true
7792     });
7793 };
7794
7795 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7796
7797      /**
7798      * @cfg {String} method
7799      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7800      */
7801     method : 'POST',
7802     /**
7803      * @cfg {String} url
7804      * The URL to use for form actions if one isn't supplied in the action options.
7805      */
7806     /**
7807      * @cfg {Boolean} fileUpload
7808      * Set to true if this form is a file upload.
7809      */
7810
7811     /**
7812      * @cfg {Object} baseParams
7813      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7814      */
7815
7816     /**
7817      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7818      */
7819     timeout: 30,
7820     /**
7821      * @cfg {Sting} align (left|right) for navbar forms
7822      */
7823     align : 'left',
7824
7825     // private
7826     activeAction : null,
7827
7828     /**
7829      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7830      * element by passing it or its id or mask the form itself by passing in true.
7831      * @type Mixed
7832      */
7833     waitMsgTarget : false,
7834
7835     loadMask : true,
7836     
7837     /**
7838      * @cfg {Boolean} errorMask (true|false) default false
7839      */
7840     errorMask : false,
7841     
7842     /**
7843      * @cfg {Number} maskOffset Default 100
7844      */
7845     maskOffset : 100,
7846     
7847     /**
7848      * @cfg {Boolean} maskBody
7849      */
7850     maskBody : false,
7851
7852     getAutoCreate : function(){
7853
7854         var cfg = {
7855             tag: 'form',
7856             method : this.method || 'POST',
7857             id : this.id || Roo.id(),
7858             cls : ''
7859         };
7860         if (this.parent().xtype.match(/^Nav/)) {
7861             cfg.cls = 'navbar-form navbar-' + this.align;
7862
7863         }
7864
7865         if (this.labelAlign == 'left' ) {
7866             cfg.cls += ' form-horizontal';
7867         }
7868
7869
7870         return cfg;
7871     },
7872     initEvents : function()
7873     {
7874         this.el.on('submit', this.onSubmit, this);
7875         // this was added as random key presses on the form where triggering form submit.
7876         this.el.on('keypress', function(e) {
7877             if (e.getCharCode() != 13) {
7878                 return true;
7879             }
7880             // we might need to allow it for textareas.. and some other items.
7881             // check e.getTarget().
7882
7883             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7884                 return true;
7885             }
7886
7887             Roo.log("keypress blocked");
7888
7889             e.preventDefault();
7890             return false;
7891         });
7892         
7893     },
7894     // private
7895     onSubmit : function(e){
7896         e.stopEvent();
7897     },
7898
7899      /**
7900      * Returns true if client-side validation on the form is successful.
7901      * @return Boolean
7902      */
7903     isValid : function(){
7904         var items = this.getItems();
7905         var valid = true;
7906         var target = false;
7907         
7908         items.each(function(f){
7909             
7910             if(f.validate()){
7911                 return;
7912             }
7913             
7914             Roo.log('invalid field: ' + f.name);
7915             
7916             valid = false;
7917
7918             if(!target && f.el.isVisible(true)){
7919                 target = f;
7920             }
7921            
7922         });
7923         
7924         if(this.errorMask && !valid){
7925             Roo.bootstrap.Form.popover.mask(this, target);
7926         }
7927         
7928         return valid;
7929     },
7930     
7931     /**
7932      * Returns true if any fields in this form have changed since their original load.
7933      * @return Boolean
7934      */
7935     isDirty : function(){
7936         var dirty = false;
7937         var items = this.getItems();
7938         items.each(function(f){
7939            if(f.isDirty()){
7940                dirty = true;
7941                return false;
7942            }
7943            return true;
7944         });
7945         return dirty;
7946     },
7947      /**
7948      * Performs a predefined action (submit or load) or custom actions you define on this form.
7949      * @param {String} actionName The name of the action type
7950      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7951      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7952      * accept other config options):
7953      * <pre>
7954 Property          Type             Description
7955 ----------------  ---------------  ----------------------------------------------------------------------------------
7956 url               String           The url for the action (defaults to the form's url)
7957 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7958 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7959 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7960                                    validate the form on the client (defaults to false)
7961      * </pre>
7962      * @return {BasicForm} this
7963      */
7964     doAction : function(action, options){
7965         if(typeof action == 'string'){
7966             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7967         }
7968         if(this.fireEvent('beforeaction', this, action) !== false){
7969             this.beforeAction(action);
7970             action.run.defer(100, action);
7971         }
7972         return this;
7973     },
7974
7975     // private
7976     beforeAction : function(action){
7977         var o = action.options;
7978         
7979         if(this.loadMask){
7980             
7981             if(this.maskBody){
7982                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7983             } else {
7984                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7985             }
7986         }
7987         // not really supported yet.. ??
7988
7989         //if(this.waitMsgTarget === true){
7990         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7991         //}else if(this.waitMsgTarget){
7992         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7993         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7994         //}else {
7995         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7996        // }
7997
7998     },
7999
8000     // private
8001     afterAction : function(action, success){
8002         this.activeAction = null;
8003         var o = action.options;
8004
8005         if(this.loadMask){
8006             
8007             if(this.maskBody){
8008                 Roo.get(document.body).unmask();
8009             } else {
8010                 this.el.unmask();
8011             }
8012         }
8013         
8014         //if(this.waitMsgTarget === true){
8015 //            this.el.unmask();
8016         //}else if(this.waitMsgTarget){
8017         //    this.waitMsgTarget.unmask();
8018         //}else{
8019         //    Roo.MessageBox.updateProgress(1);
8020         //    Roo.MessageBox.hide();
8021        // }
8022         //
8023         if(success){
8024             if(o.reset){
8025                 this.reset();
8026             }
8027             Roo.callback(o.success, o.scope, [this, action]);
8028             this.fireEvent('actioncomplete', this, action);
8029
8030         }else{
8031
8032             // failure condition..
8033             // we have a scenario where updates need confirming.
8034             // eg. if a locking scenario exists..
8035             // we look for { errors : { needs_confirm : true }} in the response.
8036             if (
8037                 (typeof(action.result) != 'undefined')  &&
8038                 (typeof(action.result.errors) != 'undefined')  &&
8039                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8040            ){
8041                 var _t = this;
8042                 Roo.log("not supported yet");
8043                  /*
8044
8045                 Roo.MessageBox.confirm(
8046                     "Change requires confirmation",
8047                     action.result.errorMsg,
8048                     function(r) {
8049                         if (r != 'yes') {
8050                             return;
8051                         }
8052                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8053                     }
8054
8055                 );
8056                 */
8057
8058
8059                 return;
8060             }
8061
8062             Roo.callback(o.failure, o.scope, [this, action]);
8063             // show an error message if no failed handler is set..
8064             if (!this.hasListener('actionfailed')) {
8065                 Roo.log("need to add dialog support");
8066                 /*
8067                 Roo.MessageBox.alert("Error",
8068                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8069                         action.result.errorMsg :
8070                         "Saving Failed, please check your entries or try again"
8071                 );
8072                 */
8073             }
8074
8075             this.fireEvent('actionfailed', this, action);
8076         }
8077
8078     },
8079     /**
8080      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8081      * @param {String} id The value to search for
8082      * @return Field
8083      */
8084     findField : function(id){
8085         var items = this.getItems();
8086         var field = items.get(id);
8087         if(!field){
8088              items.each(function(f){
8089                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8090                     field = f;
8091                     return false;
8092                 }
8093                 return true;
8094             });
8095         }
8096         return field || null;
8097     },
8098      /**
8099      * Mark fields in this form invalid in bulk.
8100      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8101      * @return {BasicForm} this
8102      */
8103     markInvalid : function(errors){
8104         if(errors instanceof Array){
8105             for(var i = 0, len = errors.length; i < len; i++){
8106                 var fieldError = errors[i];
8107                 var f = this.findField(fieldError.id);
8108                 if(f){
8109                     f.markInvalid(fieldError.msg);
8110                 }
8111             }
8112         }else{
8113             var field, id;
8114             for(id in errors){
8115                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8116                     field.markInvalid(errors[id]);
8117                 }
8118             }
8119         }
8120         //Roo.each(this.childForms || [], function (f) {
8121         //    f.markInvalid(errors);
8122         //});
8123
8124         return this;
8125     },
8126
8127     /**
8128      * Set values for fields in this form in bulk.
8129      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8130      * @return {BasicForm} this
8131      */
8132     setValues : function(values){
8133         if(values instanceof Array){ // array of objects
8134             for(var i = 0, len = values.length; i < len; i++){
8135                 var v = values[i];
8136                 var f = this.findField(v.id);
8137                 if(f){
8138                     f.setValue(v.value);
8139                     if(this.trackResetOnLoad){
8140                         f.originalValue = f.getValue();
8141                     }
8142                 }
8143             }
8144         }else{ // object hash
8145             var field, id;
8146             for(id in values){
8147                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8148
8149                     if (field.setFromData &&
8150                         field.valueField &&
8151                         field.displayField &&
8152                         // combos' with local stores can
8153                         // be queried via setValue()
8154                         // to set their value..
8155                         (field.store && !field.store.isLocal)
8156                         ) {
8157                         // it's a combo
8158                         var sd = { };
8159                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8160                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8161                         field.setFromData(sd);
8162
8163                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8164                         
8165                         field.setFromData(values);
8166                         
8167                     } else {
8168                         field.setValue(values[id]);
8169                     }
8170
8171
8172                     if(this.trackResetOnLoad){
8173                         field.originalValue = field.getValue();
8174                     }
8175                 }
8176             }
8177         }
8178
8179         //Roo.each(this.childForms || [], function (f) {
8180         //    f.setValues(values);
8181         //});
8182
8183         return this;
8184     },
8185
8186     /**
8187      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8188      * they are returned as an array.
8189      * @param {Boolean} asString
8190      * @return {Object}
8191      */
8192     getValues : function(asString){
8193         //if (this.childForms) {
8194             // copy values from the child forms
8195         //    Roo.each(this.childForms, function (f) {
8196         //        this.setValues(f.getValues());
8197         //    }, this);
8198         //}
8199
8200
8201
8202         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8203         if(asString === true){
8204             return fs;
8205         }
8206         return Roo.urlDecode(fs);
8207     },
8208
8209     /**
8210      * Returns the fields in this form as an object with key/value pairs.
8211      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8212      * @return {Object}
8213      */
8214     getFieldValues : function(with_hidden)
8215     {
8216         var items = this.getItems();
8217         var ret = {};
8218         items.each(function(f){
8219             
8220             if (!f.getName()) {
8221                 return;
8222             }
8223             
8224             var v = f.getValue();
8225             
8226             if (f.inputType =='radio') {
8227                 if (typeof(ret[f.getName()]) == 'undefined') {
8228                     ret[f.getName()] = ''; // empty..
8229                 }
8230
8231                 if (!f.el.dom.checked) {
8232                     return;
8233
8234                 }
8235                 v = f.el.dom.value;
8236
8237             }
8238             
8239             if(f.xtype == 'MoneyField'){
8240                 ret[f.currencyName] = f.getCurrency();
8241             }
8242
8243             // not sure if this supported any more..
8244             if ((typeof(v) == 'object') && f.getRawValue) {
8245                 v = f.getRawValue() ; // dates..
8246             }
8247             // combo boxes where name != hiddenName...
8248             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8249                 ret[f.name] = f.getRawValue();
8250             }
8251             ret[f.getName()] = v;
8252         });
8253
8254         return ret;
8255     },
8256
8257     /**
8258      * Clears all invalid messages in this form.
8259      * @return {BasicForm} this
8260      */
8261     clearInvalid : function(){
8262         var items = this.getItems();
8263
8264         items.each(function(f){
8265            f.clearInvalid();
8266         });
8267
8268         return this;
8269     },
8270
8271     /**
8272      * Resets this form.
8273      * @return {BasicForm} this
8274      */
8275     reset : function(){
8276         var items = this.getItems();
8277         items.each(function(f){
8278             f.reset();
8279         });
8280
8281         Roo.each(this.childForms || [], function (f) {
8282             f.reset();
8283         });
8284
8285
8286         return this;
8287     },
8288     
8289     getItems : function()
8290     {
8291         var r=new Roo.util.MixedCollection(false, function(o){
8292             return o.id || (o.id = Roo.id());
8293         });
8294         var iter = function(el) {
8295             if (el.inputEl) {
8296                 r.add(el);
8297             }
8298             if (!el.items) {
8299                 return;
8300             }
8301             Roo.each(el.items,function(e) {
8302                 iter(e);
8303             });
8304         };
8305
8306         iter(this);
8307         return r;
8308     },
8309     
8310     hideFields : function(items)
8311     {
8312         Roo.each(items, function(i){
8313             
8314             var f = this.findField(i);
8315             
8316             if(!f){
8317                 return;
8318             }
8319             
8320             f.hide();
8321             
8322         }, this);
8323     },
8324     
8325     showFields : function(items)
8326     {
8327         Roo.each(items, function(i){
8328             
8329             var f = this.findField(i);
8330             
8331             if(!f){
8332                 return;
8333             }
8334             
8335             f.show();
8336             
8337         }, this);
8338     }
8339
8340 });
8341
8342 Roo.apply(Roo.bootstrap.Form, {
8343     
8344     popover : {
8345         
8346         padding : 5,
8347         
8348         isApplied : false,
8349         
8350         isMasked : false,
8351         
8352         form : false,
8353         
8354         target : false,
8355         
8356         toolTip : false,
8357         
8358         intervalID : false,
8359         
8360         maskEl : false,
8361         
8362         apply : function()
8363         {
8364             if(this.isApplied){
8365                 return;
8366             }
8367             
8368             this.maskEl = {
8369                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8370                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8371                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8372                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8373             };
8374             
8375             this.maskEl.top.enableDisplayMode("block");
8376             this.maskEl.left.enableDisplayMode("block");
8377             this.maskEl.bottom.enableDisplayMode("block");
8378             this.maskEl.right.enableDisplayMode("block");
8379             
8380             this.toolTip = new Roo.bootstrap.Tooltip({
8381                 cls : 'roo-form-error-popover',
8382                 alignment : {
8383                     'left' : ['r-l', [-2,0], 'right'],
8384                     'right' : ['l-r', [2,0], 'left'],
8385                     'bottom' : ['tl-bl', [0,2], 'top'],
8386                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8387                 }
8388             });
8389             
8390             this.toolTip.render(Roo.get(document.body));
8391
8392             this.toolTip.el.enableDisplayMode("block");
8393             
8394             Roo.get(document.body).on('click', function(){
8395                 this.unmask();
8396             }, this);
8397             
8398             Roo.get(document.body).on('touchstart', function(){
8399                 this.unmask();
8400             }, this);
8401             
8402             this.isApplied = true
8403         },
8404         
8405         mask : function(form, target)
8406         {
8407             this.form = form;
8408             
8409             this.target = target;
8410             
8411             if(!this.form.errorMask || !target.el){
8412                 return;
8413             }
8414             
8415             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8416             
8417             Roo.log(scrollable);
8418             
8419             var ot = this.target.el.calcOffsetsTo(scrollable);
8420             
8421             var scrollTo = ot[1] - this.form.maskOffset;
8422             
8423             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8424             
8425             scrollable.scrollTo('top', scrollTo);
8426             
8427             var box = this.target.el.getBox();
8428             Roo.log(box);
8429             var zIndex = Roo.bootstrap.Modal.zIndex++;
8430
8431             
8432             this.maskEl.top.setStyle('position', 'absolute');
8433             this.maskEl.top.setStyle('z-index', zIndex);
8434             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8435             this.maskEl.top.setLeft(0);
8436             this.maskEl.top.setTop(0);
8437             this.maskEl.top.show();
8438             
8439             this.maskEl.left.setStyle('position', 'absolute');
8440             this.maskEl.left.setStyle('z-index', zIndex);
8441             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8442             this.maskEl.left.setLeft(0);
8443             this.maskEl.left.setTop(box.y - this.padding);
8444             this.maskEl.left.show();
8445
8446             this.maskEl.bottom.setStyle('position', 'absolute');
8447             this.maskEl.bottom.setStyle('z-index', zIndex);
8448             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8449             this.maskEl.bottom.setLeft(0);
8450             this.maskEl.bottom.setTop(box.bottom + this.padding);
8451             this.maskEl.bottom.show();
8452
8453             this.maskEl.right.setStyle('position', 'absolute');
8454             this.maskEl.right.setStyle('z-index', zIndex);
8455             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8456             this.maskEl.right.setLeft(box.right + this.padding);
8457             this.maskEl.right.setTop(box.y - this.padding);
8458             this.maskEl.right.show();
8459
8460             this.toolTip.bindEl = this.target.el;
8461
8462             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8463
8464             var tip = this.target.blankText;
8465
8466             if(this.target.getValue() !== '' ) {
8467                 
8468                 if (this.target.invalidText.length) {
8469                     tip = this.target.invalidText;
8470                 } else if (this.target.regexText.length){
8471                     tip = this.target.regexText;
8472                 }
8473             }
8474
8475             this.toolTip.show(tip);
8476
8477             this.intervalID = window.setInterval(function() {
8478                 Roo.bootstrap.Form.popover.unmask();
8479             }, 10000);
8480
8481             window.onwheel = function(){ return false;};
8482             
8483             (function(){ this.isMasked = true; }).defer(500, this);
8484             
8485         },
8486         
8487         unmask : function()
8488         {
8489             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8490                 return;
8491             }
8492             
8493             this.maskEl.top.setStyle('position', 'absolute');
8494             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8495             this.maskEl.top.hide();
8496
8497             this.maskEl.left.setStyle('position', 'absolute');
8498             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8499             this.maskEl.left.hide();
8500
8501             this.maskEl.bottom.setStyle('position', 'absolute');
8502             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8503             this.maskEl.bottom.hide();
8504
8505             this.maskEl.right.setStyle('position', 'absolute');
8506             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8507             this.maskEl.right.hide();
8508             
8509             this.toolTip.hide();
8510             
8511             this.toolTip.el.hide();
8512             
8513             window.onwheel = function(){ return true;};
8514             
8515             if(this.intervalID){
8516                 window.clearInterval(this.intervalID);
8517                 this.intervalID = false;
8518             }
8519             
8520             this.isMasked = false;
8521             
8522         }
8523         
8524     }
8525     
8526 });
8527
8528 /*
8529  * Based on:
8530  * Ext JS Library 1.1.1
8531  * Copyright(c) 2006-2007, Ext JS, LLC.
8532  *
8533  * Originally Released Under LGPL - original licence link has changed is not relivant.
8534  *
8535  * Fork - LGPL
8536  * <script type="text/javascript">
8537  */
8538 /**
8539  * @class Roo.form.VTypes
8540  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8541  * @singleton
8542  */
8543 Roo.form.VTypes = function(){
8544     // closure these in so they are only created once.
8545     var alpha = /^[a-zA-Z_]+$/;
8546     var alphanum = /^[a-zA-Z0-9_]+$/;
8547     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8548     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8549
8550     // All these messages and functions are configurable
8551     return {
8552         /**
8553          * The function used to validate email addresses
8554          * @param {String} value The email address
8555          */
8556         'email' : function(v){
8557             return email.test(v);
8558         },
8559         /**
8560          * The error text to display when the email validation function returns false
8561          * @type String
8562          */
8563         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8564         /**
8565          * The keystroke filter mask to be applied on email input
8566          * @type RegExp
8567          */
8568         'emailMask' : /[a-z0-9_\.\-@]/i,
8569
8570         /**
8571          * The function used to validate URLs
8572          * @param {String} value The URL
8573          */
8574         'url' : function(v){
8575             return url.test(v);
8576         },
8577         /**
8578          * The error text to display when the url validation function returns false
8579          * @type String
8580          */
8581         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8582         
8583         /**
8584          * The function used to validate alpha values
8585          * @param {String} value The value
8586          */
8587         'alpha' : function(v){
8588             return alpha.test(v);
8589         },
8590         /**
8591          * The error text to display when the alpha validation function returns false
8592          * @type String
8593          */
8594         'alphaText' : 'This field should only contain letters and _',
8595         /**
8596          * The keystroke filter mask to be applied on alpha input
8597          * @type RegExp
8598          */
8599         'alphaMask' : /[a-z_]/i,
8600
8601         /**
8602          * The function used to validate alphanumeric values
8603          * @param {String} value The value
8604          */
8605         'alphanum' : function(v){
8606             return alphanum.test(v);
8607         },
8608         /**
8609          * The error text to display when the alphanumeric validation function returns false
8610          * @type String
8611          */
8612         'alphanumText' : 'This field should only contain letters, numbers and _',
8613         /**
8614          * The keystroke filter mask to be applied on alphanumeric input
8615          * @type RegExp
8616          */
8617         'alphanumMask' : /[a-z0-9_]/i
8618     };
8619 }();/*
8620  * - LGPL
8621  *
8622  * Input
8623  * 
8624  */
8625
8626 /**
8627  * @class Roo.bootstrap.Input
8628  * @extends Roo.bootstrap.Component
8629  * Bootstrap Input class
8630  * @cfg {Boolean} disabled is it disabled
8631  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8632  * @cfg {String} name name of the input
8633  * @cfg {string} fieldLabel - the label associated
8634  * @cfg {string} placeholder - placeholder to put in text.
8635  * @cfg {string}  before - input group add on before
8636  * @cfg {string} after - input group add on after
8637  * @cfg {string} size - (lg|sm) or leave empty..
8638  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8639  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8640  * @cfg {Number} md colspan out of 12 for computer-sized screens
8641  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8642  * @cfg {string} value default value of the input
8643  * @cfg {Number} labelWidth set the width of label 
8644  * @cfg {Number} labellg set the width of label (1-12)
8645  * @cfg {Number} labelmd set the width of label (1-12)
8646  * @cfg {Number} labelsm set the width of label (1-12)
8647  * @cfg {Number} labelxs set the width of label (1-12)
8648  * @cfg {String} labelAlign (top|left)
8649  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8650  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8651  * @cfg {String} indicatorpos (left|right) default left
8652  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8653  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8654
8655  * @cfg {String} align (left|center|right) Default left
8656  * @cfg {Boolean} forceFeedback (true|false) Default false
8657  * 
8658  * @constructor
8659  * Create a new Input
8660  * @param {Object} config The config object
8661  */
8662
8663 Roo.bootstrap.Input = function(config){
8664     
8665     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8666     
8667     this.addEvents({
8668         /**
8669          * @event focus
8670          * Fires when this field receives input focus.
8671          * @param {Roo.form.Field} this
8672          */
8673         focus : true,
8674         /**
8675          * @event blur
8676          * Fires when this field loses input focus.
8677          * @param {Roo.form.Field} this
8678          */
8679         blur : true,
8680         /**
8681          * @event specialkey
8682          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8683          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8684          * @param {Roo.form.Field} this
8685          * @param {Roo.EventObject} e The event object
8686          */
8687         specialkey : true,
8688         /**
8689          * @event change
8690          * Fires just before the field blurs if the field value has changed.
8691          * @param {Roo.form.Field} this
8692          * @param {Mixed} newValue The new value
8693          * @param {Mixed} oldValue The original value
8694          */
8695         change : true,
8696         /**
8697          * @event invalid
8698          * Fires after the field has been marked as invalid.
8699          * @param {Roo.form.Field} this
8700          * @param {String} msg The validation message
8701          */
8702         invalid : true,
8703         /**
8704          * @event valid
8705          * Fires after the field has been validated with no errors.
8706          * @param {Roo.form.Field} this
8707          */
8708         valid : true,
8709          /**
8710          * @event keyup
8711          * Fires after the key up
8712          * @param {Roo.form.Field} this
8713          * @param {Roo.EventObject}  e The event Object
8714          */
8715         keyup : true
8716     });
8717 };
8718
8719 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8720      /**
8721      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8722       automatic validation (defaults to "keyup").
8723      */
8724     validationEvent : "keyup",
8725      /**
8726      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8727      */
8728     validateOnBlur : true,
8729     /**
8730      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8731      */
8732     validationDelay : 250,
8733      /**
8734      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8735      */
8736     focusClass : "x-form-focus",  // not needed???
8737     
8738        
8739     /**
8740      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8741      */
8742     invalidClass : "has-warning",
8743     
8744     /**
8745      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8746      */
8747     validClass : "has-success",
8748     
8749     /**
8750      * @cfg {Boolean} hasFeedback (true|false) default true
8751      */
8752     hasFeedback : true,
8753     
8754     /**
8755      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8756      */
8757     invalidFeedbackClass : "glyphicon-warning-sign",
8758     
8759     /**
8760      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8761      */
8762     validFeedbackClass : "glyphicon-ok",
8763     
8764     /**
8765      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8766      */
8767     selectOnFocus : false,
8768     
8769      /**
8770      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8771      */
8772     maskRe : null,
8773        /**
8774      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8775      */
8776     vtype : null,
8777     
8778       /**
8779      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8780      */
8781     disableKeyFilter : false,
8782     
8783        /**
8784      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8785      */
8786     disabled : false,
8787      /**
8788      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8789      */
8790     allowBlank : true,
8791     /**
8792      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8793      */
8794     blankText : "Please complete this mandatory field",
8795     
8796      /**
8797      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8798      */
8799     minLength : 0,
8800     /**
8801      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8802      */
8803     maxLength : Number.MAX_VALUE,
8804     /**
8805      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8806      */
8807     minLengthText : "The minimum length for this field is {0}",
8808     /**
8809      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8810      */
8811     maxLengthText : "The maximum length for this field is {0}",
8812   
8813     
8814     /**
8815      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8816      * If available, this function will be called only after the basic validators all return true, and will be passed the
8817      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8818      */
8819     validator : null,
8820     /**
8821      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8822      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8823      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8824      */
8825     regex : null,
8826     /**
8827      * @cfg {String} regexText -- Depricated - use Invalid Text
8828      */
8829     regexText : "",
8830     
8831     /**
8832      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8833      */
8834     invalidText : "",
8835     
8836     
8837     
8838     autocomplete: false,
8839     
8840     
8841     fieldLabel : '',
8842     inputType : 'text',
8843     
8844     name : false,
8845     placeholder: false,
8846     before : false,
8847     after : false,
8848     size : false,
8849     hasFocus : false,
8850     preventMark: false,
8851     isFormField : true,
8852     value : '',
8853     labelWidth : 2,
8854     labelAlign : false,
8855     readOnly : false,
8856     align : false,
8857     formatedValue : false,
8858     forceFeedback : false,
8859     
8860     indicatorpos : 'left',
8861     
8862     labellg : 0,
8863     labelmd : 0,
8864     labelsm : 0,
8865     labelxs : 0,
8866     
8867     capture : '',
8868     accept : '',
8869     
8870     parentLabelAlign : function()
8871     {
8872         var parent = this;
8873         while (parent.parent()) {
8874             parent = parent.parent();
8875             if (typeof(parent.labelAlign) !='undefined') {
8876                 return parent.labelAlign;
8877             }
8878         }
8879         return 'left';
8880         
8881     },
8882     
8883     getAutoCreate : function()
8884     {
8885         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8886         
8887         var id = Roo.id();
8888         
8889         var cfg = {};
8890         
8891         if(this.inputType != 'hidden'){
8892             cfg.cls = 'form-group' //input-group
8893         }
8894         
8895         var input =  {
8896             tag: 'input',
8897             id : id,
8898             type : this.inputType,
8899             value : this.value,
8900             cls : 'form-control',
8901             placeholder : this.placeholder || '',
8902             autocomplete : this.autocomplete || 'new-password'
8903         };
8904         
8905         if(this.capture.length){
8906             input.capture = this.capture;
8907         }
8908         
8909         if(this.accept.length){
8910             input.accept = this.accept + "/*";
8911         }
8912         
8913         if(this.align){
8914             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8915         }
8916         
8917         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8918             input.maxLength = this.maxLength;
8919         }
8920         
8921         if (this.disabled) {
8922             input.disabled=true;
8923         }
8924         
8925         if (this.readOnly) {
8926             input.readonly=true;
8927         }
8928         
8929         if (this.name) {
8930             input.name = this.name;
8931         }
8932         
8933         if (this.size) {
8934             input.cls += ' input-' + this.size;
8935         }
8936         
8937         var settings=this;
8938         ['xs','sm','md','lg'].map(function(size){
8939             if (settings[size]) {
8940                 cfg.cls += ' col-' + size + '-' + settings[size];
8941             }
8942         });
8943         
8944         var inputblock = input;
8945         
8946         var feedback = {
8947             tag: 'span',
8948             cls: 'glyphicon form-control-feedback'
8949         };
8950             
8951         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8952             
8953             inputblock = {
8954                 cls : 'has-feedback',
8955                 cn :  [
8956                     input,
8957                     feedback
8958                 ] 
8959             };  
8960         }
8961         
8962         if (this.before || this.after) {
8963             
8964             inputblock = {
8965                 cls : 'input-group',
8966                 cn :  [] 
8967             };
8968             
8969             if (this.before && typeof(this.before) == 'string') {
8970                 
8971                 inputblock.cn.push({
8972                     tag :'span',
8973                     cls : 'roo-input-before input-group-addon',
8974                     html : this.before
8975                 });
8976             }
8977             if (this.before && typeof(this.before) == 'object') {
8978                 this.before = Roo.factory(this.before);
8979                 
8980                 inputblock.cn.push({
8981                     tag :'span',
8982                     cls : 'roo-input-before input-group-' +
8983                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8984                 });
8985             }
8986             
8987             inputblock.cn.push(input);
8988             
8989             if (this.after && typeof(this.after) == 'string') {
8990                 inputblock.cn.push({
8991                     tag :'span',
8992                     cls : 'roo-input-after input-group-addon',
8993                     html : this.after
8994                 });
8995             }
8996             if (this.after && typeof(this.after) == 'object') {
8997                 this.after = Roo.factory(this.after);
8998                 
8999                 inputblock.cn.push({
9000                     tag :'span',
9001                     cls : 'roo-input-after input-group-' +
9002                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9003                 });
9004             }
9005             
9006             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9007                 inputblock.cls += ' has-feedback';
9008                 inputblock.cn.push(feedback);
9009             }
9010         };
9011         
9012         if (align ==='left' && this.fieldLabel.length) {
9013             
9014             cfg.cls += ' roo-form-group-label-left';
9015             
9016             cfg.cn = [
9017                 {
9018                     tag : 'i',
9019                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9020                     tooltip : 'This field is required'
9021                 },
9022                 {
9023                     tag: 'label',
9024                     'for' :  id,
9025                     cls : 'control-label',
9026                     html : this.fieldLabel
9027
9028                 },
9029                 {
9030                     cls : "", 
9031                     cn: [
9032                         inputblock
9033                     ]
9034                 }
9035             ];
9036             
9037             var labelCfg = cfg.cn[1];
9038             var contentCfg = cfg.cn[2];
9039             
9040             if(this.indicatorpos == 'right'){
9041                 cfg.cn = [
9042                     {
9043                         tag: 'label',
9044                         'for' :  id,
9045                         cls : 'control-label',
9046                         cn : [
9047                             {
9048                                 tag : 'span',
9049                                 html : this.fieldLabel
9050                             },
9051                             {
9052                                 tag : 'i',
9053                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9054                                 tooltip : 'This field is required'
9055                             }
9056                         ]
9057                     },
9058                     {
9059                         cls : "",
9060                         cn: [
9061                             inputblock
9062                         ]
9063                     }
9064
9065                 ];
9066                 
9067                 labelCfg = cfg.cn[0];
9068                 contentCfg = cfg.cn[1];
9069             
9070             }
9071             
9072             if(this.labelWidth > 12){
9073                 labelCfg.style = "width: " + this.labelWidth + 'px';
9074             }
9075             
9076             if(this.labelWidth < 13 && this.labelmd == 0){
9077                 this.labelmd = this.labelWidth;
9078             }
9079             
9080             if(this.labellg > 0){
9081                 labelCfg.cls += ' col-lg-' + this.labellg;
9082                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9083             }
9084             
9085             if(this.labelmd > 0){
9086                 labelCfg.cls += ' col-md-' + this.labelmd;
9087                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9088             }
9089             
9090             if(this.labelsm > 0){
9091                 labelCfg.cls += ' col-sm-' + this.labelsm;
9092                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9093             }
9094             
9095             if(this.labelxs > 0){
9096                 labelCfg.cls += ' col-xs-' + this.labelxs;
9097                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9098             }
9099             
9100             
9101         } else if ( this.fieldLabel.length) {
9102                 
9103             cfg.cn = [
9104                 {
9105                     tag : 'i',
9106                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9107                     tooltip : 'This field is required'
9108                 },
9109                 {
9110                     tag: 'label',
9111                    //cls : 'input-group-addon',
9112                     html : this.fieldLabel
9113
9114                 },
9115
9116                inputblock
9117
9118            ];
9119            
9120            if(this.indicatorpos == 'right'){
9121                 
9122                 cfg.cn = [
9123                     {
9124                         tag: 'label',
9125                        //cls : 'input-group-addon',
9126                         html : this.fieldLabel
9127
9128                     },
9129                     {
9130                         tag : 'i',
9131                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9132                         tooltip : 'This field is required'
9133                     },
9134
9135                    inputblock
9136
9137                ];
9138
9139             }
9140
9141         } else {
9142             
9143             cfg.cn = [
9144
9145                     inputblock
9146
9147             ];
9148                 
9149                 
9150         };
9151         
9152         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9153            cfg.cls += ' navbar-form';
9154         }
9155         
9156         if (this.parentType === 'NavGroup') {
9157            cfg.cls += ' navbar-form';
9158            cfg.tag = 'li';
9159         }
9160         
9161         return cfg;
9162         
9163     },
9164     /**
9165      * return the real input element.
9166      */
9167     inputEl: function ()
9168     {
9169         return this.el.select('input.form-control',true).first();
9170     },
9171     
9172     tooltipEl : function()
9173     {
9174         return this.inputEl();
9175     },
9176     
9177     indicatorEl : function()
9178     {
9179         var indicator = this.el.select('i.roo-required-indicator',true).first();
9180         
9181         if(!indicator){
9182             return false;
9183         }
9184         
9185         return indicator;
9186         
9187     },
9188     
9189     setDisabled : function(v)
9190     {
9191         var i  = this.inputEl().dom;
9192         if (!v) {
9193             i.removeAttribute('disabled');
9194             return;
9195             
9196         }
9197         i.setAttribute('disabled','true');
9198     },
9199     initEvents : function()
9200     {
9201           
9202         this.inputEl().on("keydown" , this.fireKey,  this);
9203         this.inputEl().on("focus", this.onFocus,  this);
9204         this.inputEl().on("blur", this.onBlur,  this);
9205         
9206         this.inputEl().relayEvent('keyup', this);
9207         
9208         this.indicator = this.indicatorEl();
9209         
9210         if(this.indicator){
9211             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9212         }
9213  
9214         // reference to original value for reset
9215         this.originalValue = this.getValue();
9216         //Roo.form.TextField.superclass.initEvents.call(this);
9217         if(this.validationEvent == 'keyup'){
9218             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9219             this.inputEl().on('keyup', this.filterValidation, this);
9220         }
9221         else if(this.validationEvent !== false){
9222             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9223         }
9224         
9225         if(this.selectOnFocus){
9226             this.on("focus", this.preFocus, this);
9227             
9228         }
9229         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9230             this.inputEl().on("keypress", this.filterKeys, this);
9231         } else {
9232             this.inputEl().relayEvent('keypress', this);
9233         }
9234        /* if(this.grow){
9235             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9236             this.el.on("click", this.autoSize,  this);
9237         }
9238         */
9239         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9240             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9241         }
9242         
9243         if (typeof(this.before) == 'object') {
9244             this.before.render(this.el.select('.roo-input-before',true).first());
9245         }
9246         if (typeof(this.after) == 'object') {
9247             this.after.render(this.el.select('.roo-input-after',true).first());
9248         }
9249         
9250         this.inputEl().on('change', this.onChange, this);
9251         
9252     },
9253     filterValidation : function(e){
9254         if(!e.isNavKeyPress()){
9255             this.validationTask.delay(this.validationDelay);
9256         }
9257     },
9258      /**
9259      * Validates the field value
9260      * @return {Boolean} True if the value is valid, else false
9261      */
9262     validate : function(){
9263         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9264         if(this.disabled || this.validateValue(this.getRawValue())){
9265             this.markValid();
9266             return true;
9267         }
9268         
9269         this.markInvalid();
9270         return false;
9271     },
9272     
9273     
9274     /**
9275      * Validates a value according to the field's validation rules and marks the field as invalid
9276      * if the validation fails
9277      * @param {Mixed} value The value to validate
9278      * @return {Boolean} True if the value is valid, else false
9279      */
9280     validateValue : function(value)
9281     {
9282         if(this.getVisibilityEl().hasClass('hidden')){
9283             return true;
9284         }
9285         
9286         if(value.length < 1)  { // if it's blank
9287             if(this.allowBlank){
9288                 return true;
9289             }
9290             return false;
9291         }
9292         
9293         if(value.length < this.minLength){
9294             return false;
9295         }
9296         if(value.length > this.maxLength){
9297             return false;
9298         }
9299         if(this.vtype){
9300             var vt = Roo.form.VTypes;
9301             if(!vt[this.vtype](value, this)){
9302                 return false;
9303             }
9304         }
9305         if(typeof this.validator == "function"){
9306             var msg = this.validator(value);
9307             if(msg !== true){
9308                 return false;
9309             }
9310             if (typeof(msg) == 'string') {
9311                 this.invalidText = msg;
9312             }
9313         }
9314         
9315         if(this.regex && !this.regex.test(value)){
9316             return false;
9317         }
9318         
9319         return true;
9320     },
9321     
9322      // private
9323     fireKey : function(e){
9324         //Roo.log('field ' + e.getKey());
9325         if(e.isNavKeyPress()){
9326             this.fireEvent("specialkey", this, e);
9327         }
9328     },
9329     focus : function (selectText){
9330         if(this.rendered){
9331             this.inputEl().focus();
9332             if(selectText === true){
9333                 this.inputEl().dom.select();
9334             }
9335         }
9336         return this;
9337     } ,
9338     
9339     onFocus : function(){
9340         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9341            // this.el.addClass(this.focusClass);
9342         }
9343         if(!this.hasFocus){
9344             this.hasFocus = true;
9345             this.startValue = this.getValue();
9346             this.fireEvent("focus", this);
9347         }
9348     },
9349     
9350     beforeBlur : Roo.emptyFn,
9351
9352     
9353     // private
9354     onBlur : function(){
9355         this.beforeBlur();
9356         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9357             //this.el.removeClass(this.focusClass);
9358         }
9359         this.hasFocus = false;
9360         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9361             this.validate();
9362         }
9363         var v = this.getValue();
9364         if(String(v) !== String(this.startValue)){
9365             this.fireEvent('change', this, v, this.startValue);
9366         }
9367         this.fireEvent("blur", this);
9368     },
9369     
9370     onChange : function(e)
9371     {
9372         var v = this.getValue();
9373         if(String(v) !== String(this.startValue)){
9374             this.fireEvent('change', this, v, this.startValue);
9375         }
9376         
9377     },
9378     
9379     /**
9380      * Resets the current field value to the originally loaded value and clears any validation messages
9381      */
9382     reset : function(){
9383         this.setValue(this.originalValue);
9384         this.validate();
9385     },
9386      /**
9387      * Returns the name of the field
9388      * @return {Mixed} name The name field
9389      */
9390     getName: function(){
9391         return this.name;
9392     },
9393      /**
9394      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9395      * @return {Mixed} value The field value
9396      */
9397     getValue : function(){
9398         
9399         var v = this.inputEl().getValue();
9400         
9401         return v;
9402     },
9403     /**
9404      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9405      * @return {Mixed} value The field value
9406      */
9407     getRawValue : function(){
9408         var v = this.inputEl().getValue();
9409         
9410         return v;
9411     },
9412     
9413     /**
9414      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9415      * @param {Mixed} value The value to set
9416      */
9417     setRawValue : function(v){
9418         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9419     },
9420     
9421     selectText : function(start, end){
9422         var v = this.getRawValue();
9423         if(v.length > 0){
9424             start = start === undefined ? 0 : start;
9425             end = end === undefined ? v.length : end;
9426             var d = this.inputEl().dom;
9427             if(d.setSelectionRange){
9428                 d.setSelectionRange(start, end);
9429             }else if(d.createTextRange){
9430                 var range = d.createTextRange();
9431                 range.moveStart("character", start);
9432                 range.moveEnd("character", v.length-end);
9433                 range.select();
9434             }
9435         }
9436     },
9437     
9438     /**
9439      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9440      * @param {Mixed} value The value to set
9441      */
9442     setValue : function(v){
9443         this.value = v;
9444         if(this.rendered){
9445             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9446             this.validate();
9447         }
9448     },
9449     
9450     /*
9451     processValue : function(value){
9452         if(this.stripCharsRe){
9453             var newValue = value.replace(this.stripCharsRe, '');
9454             if(newValue !== value){
9455                 this.setRawValue(newValue);
9456                 return newValue;
9457             }
9458         }
9459         return value;
9460     },
9461   */
9462     preFocus : function(){
9463         
9464         if(this.selectOnFocus){
9465             this.inputEl().dom.select();
9466         }
9467     },
9468     filterKeys : function(e){
9469         var k = e.getKey();
9470         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9471             return;
9472         }
9473         var c = e.getCharCode(), cc = String.fromCharCode(c);
9474         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9475             return;
9476         }
9477         if(!this.maskRe.test(cc)){
9478             e.stopEvent();
9479         }
9480     },
9481      /**
9482      * Clear any invalid styles/messages for this field
9483      */
9484     clearInvalid : function(){
9485         
9486         if(!this.el || this.preventMark){ // not rendered
9487             return;
9488         }
9489         
9490      
9491         this.el.removeClass(this.invalidClass);
9492         
9493         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9494             
9495             var feedback = this.el.select('.form-control-feedback', true).first();
9496             
9497             if(feedback){
9498                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9499             }
9500             
9501         }
9502         
9503         if(this.indicator){
9504             this.indicator.removeClass('visible');
9505             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9506         }
9507         
9508         this.fireEvent('valid', this);
9509     },
9510     
9511      /**
9512      * Mark this field as valid
9513      */
9514     markValid : function()
9515     {
9516         if(!this.el  || this.preventMark){ // not rendered...
9517             return;
9518         }
9519         
9520         this.el.removeClass([this.invalidClass, this.validClass]);
9521         
9522         var feedback = this.el.select('.form-control-feedback', true).first();
9523             
9524         if(feedback){
9525             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9526         }
9527         
9528         if(this.indicator){
9529             this.indicator.removeClass('visible');
9530             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9531         }
9532         
9533         if(this.disabled){
9534             return;
9535         }
9536         
9537         if(this.allowBlank && !this.getRawValue().length){
9538             return;
9539         }
9540         
9541         this.el.addClass(this.validClass);
9542         
9543         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9544             
9545             var feedback = this.el.select('.form-control-feedback', true).first();
9546             
9547             if(feedback){
9548                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9549                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9550             }
9551             
9552         }
9553         
9554         this.fireEvent('valid', this);
9555     },
9556     
9557      /**
9558      * Mark this field as invalid
9559      * @param {String} msg The validation message
9560      */
9561     markInvalid : function(msg)
9562     {
9563         if(!this.el  || this.preventMark){ // not rendered
9564             return;
9565         }
9566         
9567         this.el.removeClass([this.invalidClass, this.validClass]);
9568         
9569         var feedback = this.el.select('.form-control-feedback', true).first();
9570             
9571         if(feedback){
9572             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9573         }
9574
9575         if(this.disabled){
9576             return;
9577         }
9578         
9579         if(this.allowBlank && !this.getRawValue().length){
9580             return;
9581         }
9582         
9583         if(this.indicator){
9584             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9585             this.indicator.addClass('visible');
9586         }
9587         
9588         this.el.addClass(this.invalidClass);
9589         
9590         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9591             
9592             var feedback = this.el.select('.form-control-feedback', true).first();
9593             
9594             if(feedback){
9595                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9596                 
9597                 if(this.getValue().length || this.forceFeedback){
9598                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9599                 }
9600                 
9601             }
9602             
9603         }
9604         
9605         this.fireEvent('invalid', this, msg);
9606     },
9607     // private
9608     SafariOnKeyDown : function(event)
9609     {
9610         // this is a workaround for a password hang bug on chrome/ webkit.
9611         if (this.inputEl().dom.type != 'password') {
9612             return;
9613         }
9614         
9615         var isSelectAll = false;
9616         
9617         if(this.inputEl().dom.selectionEnd > 0){
9618             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9619         }
9620         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9621             event.preventDefault();
9622             this.setValue('');
9623             return;
9624         }
9625         
9626         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9627             
9628             event.preventDefault();
9629             // this is very hacky as keydown always get's upper case.
9630             //
9631             var cc = String.fromCharCode(event.getCharCode());
9632             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9633             
9634         }
9635     },
9636     adjustWidth : function(tag, w){
9637         tag = tag.toLowerCase();
9638         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9639             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9640                 if(tag == 'input'){
9641                     return w + 2;
9642                 }
9643                 if(tag == 'textarea'){
9644                     return w-2;
9645                 }
9646             }else if(Roo.isOpera){
9647                 if(tag == 'input'){
9648                     return w + 2;
9649                 }
9650                 if(tag == 'textarea'){
9651                     return w-2;
9652                 }
9653             }
9654         }
9655         return w;
9656     },
9657     
9658     setFieldLabel : function(v)
9659     {
9660         if(!this.rendered){
9661             return;
9662         }
9663         
9664         if(this.indicator){
9665             var ar = this.el.select('label > span',true);
9666             
9667             if (ar.elements.length) {
9668                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9669                 this.fieldLabel = v;
9670                 return;
9671             }
9672             
9673             var br = this.el.select('label',true);
9674             
9675             if(br.elements.length) {
9676                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9677                 this.fieldLabel = v;
9678                 return;
9679             }
9680             
9681             Roo.log('Cannot Found any of label > span || label in input');
9682             return;
9683         }
9684         
9685         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9686         this.fieldLabel = v;
9687         
9688         
9689     }
9690 });
9691
9692  
9693 /*
9694  * - LGPL
9695  *
9696  * Input
9697  * 
9698  */
9699
9700 /**
9701  * @class Roo.bootstrap.TextArea
9702  * @extends Roo.bootstrap.Input
9703  * Bootstrap TextArea class
9704  * @cfg {Number} cols Specifies the visible width of a text area
9705  * @cfg {Number} rows Specifies the visible number of lines in a text area
9706  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9707  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9708  * @cfg {string} html text
9709  * 
9710  * @constructor
9711  * Create a new TextArea
9712  * @param {Object} config The config object
9713  */
9714
9715 Roo.bootstrap.TextArea = function(config){
9716     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9717    
9718 };
9719
9720 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9721      
9722     cols : false,
9723     rows : 5,
9724     readOnly : false,
9725     warp : 'soft',
9726     resize : false,
9727     value: false,
9728     html: false,
9729     
9730     getAutoCreate : function(){
9731         
9732         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9733         
9734         var id = Roo.id();
9735         
9736         var cfg = {};
9737         
9738         if(this.inputType != 'hidden'){
9739             cfg.cls = 'form-group' //input-group
9740         }
9741         
9742         var input =  {
9743             tag: 'textarea',
9744             id : id,
9745             warp : this.warp,
9746             rows : this.rows,
9747             value : this.value || '',
9748             html: this.html || '',
9749             cls : 'form-control',
9750             placeholder : this.placeholder || '' 
9751             
9752         };
9753         
9754         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9755             input.maxLength = this.maxLength;
9756         }
9757         
9758         if(this.resize){
9759             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9760         }
9761         
9762         if(this.cols){
9763             input.cols = this.cols;
9764         }
9765         
9766         if (this.readOnly) {
9767             input.readonly = true;
9768         }
9769         
9770         if (this.name) {
9771             input.name = this.name;
9772         }
9773         
9774         if (this.size) {
9775             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9776         }
9777         
9778         var settings=this;
9779         ['xs','sm','md','lg'].map(function(size){
9780             if (settings[size]) {
9781                 cfg.cls += ' col-' + size + '-' + settings[size];
9782             }
9783         });
9784         
9785         var inputblock = input;
9786         
9787         if(this.hasFeedback && !this.allowBlank){
9788             
9789             var feedback = {
9790                 tag: 'span',
9791                 cls: 'glyphicon form-control-feedback'
9792             };
9793
9794             inputblock = {
9795                 cls : 'has-feedback',
9796                 cn :  [
9797                     input,
9798                     feedback
9799                 ] 
9800             };  
9801         }
9802         
9803         
9804         if (this.before || this.after) {
9805             
9806             inputblock = {
9807                 cls : 'input-group',
9808                 cn :  [] 
9809             };
9810             if (this.before) {
9811                 inputblock.cn.push({
9812                     tag :'span',
9813                     cls : 'input-group-addon',
9814                     html : this.before
9815                 });
9816             }
9817             
9818             inputblock.cn.push(input);
9819             
9820             if(this.hasFeedback && !this.allowBlank){
9821                 inputblock.cls += ' has-feedback';
9822                 inputblock.cn.push(feedback);
9823             }
9824             
9825             if (this.after) {
9826                 inputblock.cn.push({
9827                     tag :'span',
9828                     cls : 'input-group-addon',
9829                     html : this.after
9830                 });
9831             }
9832             
9833         }
9834         
9835         if (align ==='left' && this.fieldLabel.length) {
9836             cfg.cn = [
9837                 {
9838                     tag: 'label',
9839                     'for' :  id,
9840                     cls : 'control-label',
9841                     html : this.fieldLabel
9842                 },
9843                 {
9844                     cls : "",
9845                     cn: [
9846                         inputblock
9847                     ]
9848                 }
9849
9850             ];
9851             
9852             if(this.labelWidth > 12){
9853                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9854             }
9855
9856             if(this.labelWidth < 13 && this.labelmd == 0){
9857                 this.labelmd = this.labelWidth;
9858             }
9859
9860             if(this.labellg > 0){
9861                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9862                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9863             }
9864
9865             if(this.labelmd > 0){
9866                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9867                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9868             }
9869
9870             if(this.labelsm > 0){
9871                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9872                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9873             }
9874
9875             if(this.labelxs > 0){
9876                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9877                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9878             }
9879             
9880         } else if ( this.fieldLabel.length) {
9881             cfg.cn = [
9882
9883                {
9884                    tag: 'label',
9885                    //cls : 'input-group-addon',
9886                    html : this.fieldLabel
9887
9888                },
9889
9890                inputblock
9891
9892            ];
9893
9894         } else {
9895
9896             cfg.cn = [
9897
9898                 inputblock
9899
9900             ];
9901                 
9902         }
9903         
9904         if (this.disabled) {
9905             input.disabled=true;
9906         }
9907         
9908         return cfg;
9909         
9910     },
9911     /**
9912      * return the real textarea element.
9913      */
9914     inputEl: function ()
9915     {
9916         return this.el.select('textarea.form-control',true).first();
9917     },
9918     
9919     /**
9920      * Clear any invalid styles/messages for this field
9921      */
9922     clearInvalid : function()
9923     {
9924         
9925         if(!this.el || this.preventMark){ // not rendered
9926             return;
9927         }
9928         
9929         var label = this.el.select('label', true).first();
9930         var icon = this.el.select('i.fa-star', true).first();
9931         
9932         if(label && icon){
9933             icon.remove();
9934         }
9935         
9936         this.el.removeClass(this.invalidClass);
9937         
9938         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9939             
9940             var feedback = this.el.select('.form-control-feedback', true).first();
9941             
9942             if(feedback){
9943                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9944             }
9945             
9946         }
9947         
9948         this.fireEvent('valid', this);
9949     },
9950     
9951      /**
9952      * Mark this field as valid
9953      */
9954     markValid : function()
9955     {
9956         if(!this.el  || this.preventMark){ // not rendered
9957             return;
9958         }
9959         
9960         this.el.removeClass([this.invalidClass, this.validClass]);
9961         
9962         var feedback = this.el.select('.form-control-feedback', true).first();
9963             
9964         if(feedback){
9965             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9966         }
9967
9968         if(this.disabled || this.allowBlank){
9969             return;
9970         }
9971         
9972         var label = this.el.select('label', true).first();
9973         var icon = this.el.select('i.fa-star', true).first();
9974         
9975         if(label && icon){
9976             icon.remove();
9977         }
9978         
9979         this.el.addClass(this.validClass);
9980         
9981         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9982             
9983             var feedback = this.el.select('.form-control-feedback', true).first();
9984             
9985             if(feedback){
9986                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9987                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9988             }
9989             
9990         }
9991         
9992         this.fireEvent('valid', this);
9993     },
9994     
9995      /**
9996      * Mark this field as invalid
9997      * @param {String} msg The validation message
9998      */
9999     markInvalid : function(msg)
10000     {
10001         if(!this.el  || this.preventMark){ // not rendered
10002             return;
10003         }
10004         
10005         this.el.removeClass([this.invalidClass, this.validClass]);
10006         
10007         var feedback = this.el.select('.form-control-feedback', true).first();
10008             
10009         if(feedback){
10010             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10011         }
10012
10013         if(this.disabled || this.allowBlank){
10014             return;
10015         }
10016         
10017         var label = this.el.select('label', true).first();
10018         var icon = this.el.select('i.fa-star', true).first();
10019         
10020         if(!this.getValue().length && label && !icon){
10021             this.el.createChild({
10022                 tag : 'i',
10023                 cls : 'text-danger fa fa-lg fa-star',
10024                 tooltip : 'This field is required',
10025                 style : 'margin-right:5px;'
10026             }, label, true);
10027         }
10028
10029         this.el.addClass(this.invalidClass);
10030         
10031         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10032             
10033             var feedback = this.el.select('.form-control-feedback', true).first();
10034             
10035             if(feedback){
10036                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10037                 
10038                 if(this.getValue().length || this.forceFeedback){
10039                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10040                 }
10041                 
10042             }
10043             
10044         }
10045         
10046         this.fireEvent('invalid', this, msg);
10047     }
10048 });
10049
10050  
10051 /*
10052  * - LGPL
10053  *
10054  * trigger field - base class for combo..
10055  * 
10056  */
10057  
10058 /**
10059  * @class Roo.bootstrap.TriggerField
10060  * @extends Roo.bootstrap.Input
10061  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10062  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10063  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10064  * for which you can provide a custom implementation.  For example:
10065  * <pre><code>
10066 var trigger = new Roo.bootstrap.TriggerField();
10067 trigger.onTriggerClick = myTriggerFn;
10068 trigger.applyTo('my-field');
10069 </code></pre>
10070  *
10071  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10072  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10073  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10074  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10075  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10076
10077  * @constructor
10078  * Create a new TriggerField.
10079  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10080  * to the base TextField)
10081  */
10082 Roo.bootstrap.TriggerField = function(config){
10083     this.mimicing = false;
10084     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10085 };
10086
10087 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10088     /**
10089      * @cfg {String} triggerClass A CSS class to apply to the trigger
10090      */
10091      /**
10092      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10093      */
10094     hideTrigger:false,
10095
10096     /**
10097      * @cfg {Boolean} removable (true|false) special filter default false
10098      */
10099     removable : false,
10100     
10101     /** @cfg {Boolean} grow @hide */
10102     /** @cfg {Number} growMin @hide */
10103     /** @cfg {Number} growMax @hide */
10104
10105     /**
10106      * @hide 
10107      * @method
10108      */
10109     autoSize: Roo.emptyFn,
10110     // private
10111     monitorTab : true,
10112     // private
10113     deferHeight : true,
10114
10115     
10116     actionMode : 'wrap',
10117     
10118     caret : false,
10119     
10120     
10121     getAutoCreate : function(){
10122        
10123         var align = this.labelAlign || this.parentLabelAlign();
10124         
10125         var id = Roo.id();
10126         
10127         var cfg = {
10128             cls: 'form-group' //input-group
10129         };
10130         
10131         
10132         var input =  {
10133             tag: 'input',
10134             id : id,
10135             type : this.inputType,
10136             cls : 'form-control',
10137             autocomplete: 'new-password',
10138             placeholder : this.placeholder || '' 
10139             
10140         };
10141         if (this.name) {
10142             input.name = this.name;
10143         }
10144         if (this.size) {
10145             input.cls += ' input-' + this.size;
10146         }
10147         
10148         if (this.disabled) {
10149             input.disabled=true;
10150         }
10151         
10152         var inputblock = input;
10153         
10154         if(this.hasFeedback && !this.allowBlank){
10155             
10156             var feedback = {
10157                 tag: 'span',
10158                 cls: 'glyphicon form-control-feedback'
10159             };
10160             
10161             if(this.removable && !this.editable && !this.tickable){
10162                 inputblock = {
10163                     cls : 'has-feedback',
10164                     cn :  [
10165                         inputblock,
10166                         {
10167                             tag: 'button',
10168                             html : 'x',
10169                             cls : 'roo-combo-removable-btn close'
10170                         },
10171                         feedback
10172                     ] 
10173                 };
10174             } else {
10175                 inputblock = {
10176                     cls : 'has-feedback',
10177                     cn :  [
10178                         inputblock,
10179                         feedback
10180                     ] 
10181                 };
10182             }
10183
10184         } else {
10185             if(this.removable && !this.editable && !this.tickable){
10186                 inputblock = {
10187                     cls : 'roo-removable',
10188                     cn :  [
10189                         inputblock,
10190                         {
10191                             tag: 'button',
10192                             html : 'x',
10193                             cls : 'roo-combo-removable-btn close'
10194                         }
10195                     ] 
10196                 };
10197             }
10198         }
10199         
10200         if (this.before || this.after) {
10201             
10202             inputblock = {
10203                 cls : 'input-group',
10204                 cn :  [] 
10205             };
10206             if (this.before) {
10207                 inputblock.cn.push({
10208                     tag :'span',
10209                     cls : 'input-group-addon',
10210                     html : this.before
10211                 });
10212             }
10213             
10214             inputblock.cn.push(input);
10215             
10216             if(this.hasFeedback && !this.allowBlank){
10217                 inputblock.cls += ' has-feedback';
10218                 inputblock.cn.push(feedback);
10219             }
10220             
10221             if (this.after) {
10222                 inputblock.cn.push({
10223                     tag :'span',
10224                     cls : 'input-group-addon',
10225                     html : this.after
10226                 });
10227             }
10228             
10229         };
10230         
10231         var box = {
10232             tag: 'div',
10233             cn: [
10234                 {
10235                     tag: 'input',
10236                     type : 'hidden',
10237                     cls: 'form-hidden-field'
10238                 },
10239                 inputblock
10240             ]
10241             
10242         };
10243         
10244         if(this.multiple){
10245             box = {
10246                 tag: 'div',
10247                 cn: [
10248                     {
10249                         tag: 'input',
10250                         type : 'hidden',
10251                         cls: 'form-hidden-field'
10252                     },
10253                     {
10254                         tag: 'ul',
10255                         cls: 'roo-select2-choices',
10256                         cn:[
10257                             {
10258                                 tag: 'li',
10259                                 cls: 'roo-select2-search-field',
10260                                 cn: [
10261
10262                                     inputblock
10263                                 ]
10264                             }
10265                         ]
10266                     }
10267                 ]
10268             }
10269         };
10270         
10271         var combobox = {
10272             cls: 'roo-select2-container input-group',
10273             cn: [
10274                 box
10275 //                {
10276 //                    tag: 'ul',
10277 //                    cls: 'typeahead typeahead-long dropdown-menu',
10278 //                    style: 'display:none'
10279 //                }
10280             ]
10281         };
10282         
10283         if(!this.multiple && this.showToggleBtn){
10284             
10285             var caret = {
10286                         tag: 'span',
10287                         cls: 'caret'
10288              };
10289             if (this.caret != false) {
10290                 caret = {
10291                      tag: 'i',
10292                      cls: 'fa fa-' + this.caret
10293                 };
10294                 
10295             }
10296             
10297             combobox.cn.push({
10298                 tag :'span',
10299                 cls : 'input-group-addon btn dropdown-toggle',
10300                 cn : [
10301                     caret,
10302                     {
10303                         tag: 'span',
10304                         cls: 'combobox-clear',
10305                         cn  : [
10306                             {
10307                                 tag : 'i',
10308                                 cls: 'icon-remove'
10309                             }
10310                         ]
10311                     }
10312                 ]
10313
10314             })
10315         }
10316         
10317         if(this.multiple){
10318             combobox.cls += ' roo-select2-container-multi';
10319         }
10320         
10321         if (align ==='left' && this.fieldLabel.length) {
10322             
10323             cfg.cls += ' roo-form-group-label-left';
10324
10325             cfg.cn = [
10326                 {
10327                     tag : 'i',
10328                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10329                     tooltip : 'This field is required'
10330                 },
10331                 {
10332                     tag: 'label',
10333                     'for' :  id,
10334                     cls : 'control-label',
10335                     html : this.fieldLabel
10336
10337                 },
10338                 {
10339                     cls : "", 
10340                     cn: [
10341                         combobox
10342                     ]
10343                 }
10344
10345             ];
10346             
10347             var labelCfg = cfg.cn[1];
10348             var contentCfg = cfg.cn[2];
10349             
10350             if(this.indicatorpos == 'right'){
10351                 cfg.cn = [
10352                     {
10353                         tag: 'label',
10354                         'for' :  id,
10355                         cls : 'control-label',
10356                         cn : [
10357                             {
10358                                 tag : 'span',
10359                                 html : this.fieldLabel
10360                             },
10361                             {
10362                                 tag : 'i',
10363                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10364                                 tooltip : 'This field is required'
10365                             }
10366                         ]
10367                     },
10368                     {
10369                         cls : "", 
10370                         cn: [
10371                             combobox
10372                         ]
10373                     }
10374
10375                 ];
10376                 
10377                 labelCfg = cfg.cn[0];
10378                 contentCfg = cfg.cn[1];
10379             }
10380             
10381             if(this.labelWidth > 12){
10382                 labelCfg.style = "width: " + this.labelWidth + 'px';
10383             }
10384             
10385             if(this.labelWidth < 13 && this.labelmd == 0){
10386                 this.labelmd = this.labelWidth;
10387             }
10388             
10389             if(this.labellg > 0){
10390                 labelCfg.cls += ' col-lg-' + this.labellg;
10391                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10392             }
10393             
10394             if(this.labelmd > 0){
10395                 labelCfg.cls += ' col-md-' + this.labelmd;
10396                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10397             }
10398             
10399             if(this.labelsm > 0){
10400                 labelCfg.cls += ' col-sm-' + this.labelsm;
10401                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10402             }
10403             
10404             if(this.labelxs > 0){
10405                 labelCfg.cls += ' col-xs-' + this.labelxs;
10406                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10407             }
10408             
10409         } else if ( this.fieldLabel.length) {
10410 //                Roo.log(" label");
10411             cfg.cn = [
10412                 {
10413                    tag : 'i',
10414                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10415                    tooltip : 'This field is required'
10416                },
10417                {
10418                    tag: 'label',
10419                    //cls : 'input-group-addon',
10420                    html : this.fieldLabel
10421
10422                },
10423
10424                combobox
10425
10426             ];
10427             
10428             if(this.indicatorpos == 'right'){
10429                 
10430                 cfg.cn = [
10431                     {
10432                        tag: 'label',
10433                        cn : [
10434                            {
10435                                tag : 'span',
10436                                html : this.fieldLabel
10437                            },
10438                            {
10439                               tag : 'i',
10440                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10441                               tooltip : 'This field is required'
10442                            }
10443                        ]
10444
10445                     },
10446                     combobox
10447
10448                 ];
10449
10450             }
10451
10452         } else {
10453             
10454 //                Roo.log(" no label && no align");
10455                 cfg = combobox
10456                      
10457                 
10458         }
10459         
10460         var settings=this;
10461         ['xs','sm','md','lg'].map(function(size){
10462             if (settings[size]) {
10463                 cfg.cls += ' col-' + size + '-' + settings[size];
10464             }
10465         });
10466         
10467         return cfg;
10468         
10469     },
10470     
10471     
10472     
10473     // private
10474     onResize : function(w, h){
10475 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10476 //        if(typeof w == 'number'){
10477 //            var x = w - this.trigger.getWidth();
10478 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10479 //            this.trigger.setStyle('left', x+'px');
10480 //        }
10481     },
10482
10483     // private
10484     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10485
10486     // private
10487     getResizeEl : function(){
10488         return this.inputEl();
10489     },
10490
10491     // private
10492     getPositionEl : function(){
10493         return this.inputEl();
10494     },
10495
10496     // private
10497     alignErrorIcon : function(){
10498         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10499     },
10500
10501     // private
10502     initEvents : function(){
10503         
10504         this.createList();
10505         
10506         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10507         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10508         if(!this.multiple && this.showToggleBtn){
10509             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10510             if(this.hideTrigger){
10511                 this.trigger.setDisplayed(false);
10512             }
10513             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10514         }
10515         
10516         if(this.multiple){
10517             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10518         }
10519         
10520         if(this.removable && !this.editable && !this.tickable){
10521             var close = this.closeTriggerEl();
10522             
10523             if(close){
10524                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10525                 close.on('click', this.removeBtnClick, this, close);
10526             }
10527         }
10528         
10529         //this.trigger.addClassOnOver('x-form-trigger-over');
10530         //this.trigger.addClassOnClick('x-form-trigger-click');
10531         
10532         //if(!this.width){
10533         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10534         //}
10535     },
10536     
10537     closeTriggerEl : function()
10538     {
10539         var close = this.el.select('.roo-combo-removable-btn', true).first();
10540         return close ? close : false;
10541     },
10542     
10543     removeBtnClick : function(e, h, el)
10544     {
10545         e.preventDefault();
10546         
10547         if(this.fireEvent("remove", this) !== false){
10548             this.reset();
10549             this.fireEvent("afterremove", this)
10550         }
10551     },
10552     
10553     createList : function()
10554     {
10555         this.list = Roo.get(document.body).createChild({
10556             tag: 'ul',
10557             cls: 'typeahead typeahead-long dropdown-menu',
10558             style: 'display:none'
10559         });
10560         
10561         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10562         
10563     },
10564
10565     // private
10566     initTrigger : function(){
10567        
10568     },
10569
10570     // private
10571     onDestroy : function(){
10572         if(this.trigger){
10573             this.trigger.removeAllListeners();
10574           //  this.trigger.remove();
10575         }
10576         //if(this.wrap){
10577         //    this.wrap.remove();
10578         //}
10579         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10580     },
10581
10582     // private
10583     onFocus : function(){
10584         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10585         /*
10586         if(!this.mimicing){
10587             this.wrap.addClass('x-trigger-wrap-focus');
10588             this.mimicing = true;
10589             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10590             if(this.monitorTab){
10591                 this.el.on("keydown", this.checkTab, this);
10592             }
10593         }
10594         */
10595     },
10596
10597     // private
10598     checkTab : function(e){
10599         if(e.getKey() == e.TAB){
10600             this.triggerBlur();
10601         }
10602     },
10603
10604     // private
10605     onBlur : function(){
10606         // do nothing
10607     },
10608
10609     // private
10610     mimicBlur : function(e, t){
10611         /*
10612         if(!this.wrap.contains(t) && this.validateBlur()){
10613             this.triggerBlur();
10614         }
10615         */
10616     },
10617
10618     // private
10619     triggerBlur : function(){
10620         this.mimicing = false;
10621         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10622         if(this.monitorTab){
10623             this.el.un("keydown", this.checkTab, this);
10624         }
10625         //this.wrap.removeClass('x-trigger-wrap-focus');
10626         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10627     },
10628
10629     // private
10630     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10631     validateBlur : function(e, t){
10632         return true;
10633     },
10634
10635     // private
10636     onDisable : function(){
10637         this.inputEl().dom.disabled = true;
10638         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10639         //if(this.wrap){
10640         //    this.wrap.addClass('x-item-disabled');
10641         //}
10642     },
10643
10644     // private
10645     onEnable : function(){
10646         this.inputEl().dom.disabled = false;
10647         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10648         //if(this.wrap){
10649         //    this.el.removeClass('x-item-disabled');
10650         //}
10651     },
10652
10653     // private
10654     onShow : function(){
10655         var ae = this.getActionEl();
10656         
10657         if(ae){
10658             ae.dom.style.display = '';
10659             ae.dom.style.visibility = 'visible';
10660         }
10661     },
10662
10663     // private
10664     
10665     onHide : function(){
10666         var ae = this.getActionEl();
10667         ae.dom.style.display = 'none';
10668     },
10669
10670     /**
10671      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10672      * by an implementing function.
10673      * @method
10674      * @param {EventObject} e
10675      */
10676     onTriggerClick : Roo.emptyFn
10677 });
10678  /*
10679  * Based on:
10680  * Ext JS Library 1.1.1
10681  * Copyright(c) 2006-2007, Ext JS, LLC.
10682  *
10683  * Originally Released Under LGPL - original licence link has changed is not relivant.
10684  *
10685  * Fork - LGPL
10686  * <script type="text/javascript">
10687  */
10688
10689
10690 /**
10691  * @class Roo.data.SortTypes
10692  * @singleton
10693  * Defines the default sorting (casting?) comparison functions used when sorting data.
10694  */
10695 Roo.data.SortTypes = {
10696     /**
10697      * Default sort that does nothing
10698      * @param {Mixed} s The value being converted
10699      * @return {Mixed} The comparison value
10700      */
10701     none : function(s){
10702         return s;
10703     },
10704     
10705     /**
10706      * The regular expression used to strip tags
10707      * @type {RegExp}
10708      * @property
10709      */
10710     stripTagsRE : /<\/?[^>]+>/gi,
10711     
10712     /**
10713      * Strips all HTML tags to sort on text only
10714      * @param {Mixed} s The value being converted
10715      * @return {String} The comparison value
10716      */
10717     asText : function(s){
10718         return String(s).replace(this.stripTagsRE, "");
10719     },
10720     
10721     /**
10722      * Strips all HTML tags to sort on text only - Case insensitive
10723      * @param {Mixed} s The value being converted
10724      * @return {String} The comparison value
10725      */
10726     asUCText : function(s){
10727         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10728     },
10729     
10730     /**
10731      * Case insensitive string
10732      * @param {Mixed} s The value being converted
10733      * @return {String} The comparison value
10734      */
10735     asUCString : function(s) {
10736         return String(s).toUpperCase();
10737     },
10738     
10739     /**
10740      * Date sorting
10741      * @param {Mixed} s The value being converted
10742      * @return {Number} The comparison value
10743      */
10744     asDate : function(s) {
10745         if(!s){
10746             return 0;
10747         }
10748         if(s instanceof Date){
10749             return s.getTime();
10750         }
10751         return Date.parse(String(s));
10752     },
10753     
10754     /**
10755      * Float sorting
10756      * @param {Mixed} s The value being converted
10757      * @return {Float} The comparison value
10758      */
10759     asFloat : function(s) {
10760         var val = parseFloat(String(s).replace(/,/g, ""));
10761         if(isNaN(val)) {
10762             val = 0;
10763         }
10764         return val;
10765     },
10766     
10767     /**
10768      * Integer sorting
10769      * @param {Mixed} s The value being converted
10770      * @return {Number} The comparison value
10771      */
10772     asInt : function(s) {
10773         var val = parseInt(String(s).replace(/,/g, ""));
10774         if(isNaN(val)) {
10775             val = 0;
10776         }
10777         return val;
10778     }
10779 };/*
10780  * Based on:
10781  * Ext JS Library 1.1.1
10782  * Copyright(c) 2006-2007, Ext JS, LLC.
10783  *
10784  * Originally Released Under LGPL - original licence link has changed is not relivant.
10785  *
10786  * Fork - LGPL
10787  * <script type="text/javascript">
10788  */
10789
10790 /**
10791 * @class Roo.data.Record
10792  * Instances of this class encapsulate both record <em>definition</em> information, and record
10793  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10794  * to access Records cached in an {@link Roo.data.Store} object.<br>
10795  * <p>
10796  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10797  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10798  * objects.<br>
10799  * <p>
10800  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10801  * @constructor
10802  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10803  * {@link #create}. The parameters are the same.
10804  * @param {Array} data An associative Array of data values keyed by the field name.
10805  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10806  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10807  * not specified an integer id is generated.
10808  */
10809 Roo.data.Record = function(data, id){
10810     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10811     this.data = data;
10812 };
10813
10814 /**
10815  * Generate a constructor for a specific record layout.
10816  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10817  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10818  * Each field definition object may contain the following properties: <ul>
10819  * <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,
10820  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10821  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10822  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10823  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10824  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10825  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10826  * this may be omitted.</p></li>
10827  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10828  * <ul><li>auto (Default, implies no conversion)</li>
10829  * <li>string</li>
10830  * <li>int</li>
10831  * <li>float</li>
10832  * <li>boolean</li>
10833  * <li>date</li></ul></p></li>
10834  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10835  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10836  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10837  * by the Reader into an object that will be stored in the Record. It is passed the
10838  * following parameters:<ul>
10839  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10840  * </ul></p></li>
10841  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10842  * </ul>
10843  * <br>usage:<br><pre><code>
10844 var TopicRecord = Roo.data.Record.create(
10845     {name: 'title', mapping: 'topic_title'},
10846     {name: 'author', mapping: 'username'},
10847     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10848     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10849     {name: 'lastPoster', mapping: 'user2'},
10850     {name: 'excerpt', mapping: 'post_text'}
10851 );
10852
10853 var myNewRecord = new TopicRecord({
10854     title: 'Do my job please',
10855     author: 'noobie',
10856     totalPosts: 1,
10857     lastPost: new Date(),
10858     lastPoster: 'Animal',
10859     excerpt: 'No way dude!'
10860 });
10861 myStore.add(myNewRecord);
10862 </code></pre>
10863  * @method create
10864  * @static
10865  */
10866 Roo.data.Record.create = function(o){
10867     var f = function(){
10868         f.superclass.constructor.apply(this, arguments);
10869     };
10870     Roo.extend(f, Roo.data.Record);
10871     var p = f.prototype;
10872     p.fields = new Roo.util.MixedCollection(false, function(field){
10873         return field.name;
10874     });
10875     for(var i = 0, len = o.length; i < len; i++){
10876         p.fields.add(new Roo.data.Field(o[i]));
10877     }
10878     f.getField = function(name){
10879         return p.fields.get(name);  
10880     };
10881     return f;
10882 };
10883
10884 Roo.data.Record.AUTO_ID = 1000;
10885 Roo.data.Record.EDIT = 'edit';
10886 Roo.data.Record.REJECT = 'reject';
10887 Roo.data.Record.COMMIT = 'commit';
10888
10889 Roo.data.Record.prototype = {
10890     /**
10891      * Readonly flag - true if this record has been modified.
10892      * @type Boolean
10893      */
10894     dirty : false,
10895     editing : false,
10896     error: null,
10897     modified: null,
10898
10899     // private
10900     join : function(store){
10901         this.store = store;
10902     },
10903
10904     /**
10905      * Set the named field to the specified value.
10906      * @param {String} name The name of the field to set.
10907      * @param {Object} value The value to set the field to.
10908      */
10909     set : function(name, value){
10910         if(this.data[name] == value){
10911             return;
10912         }
10913         this.dirty = true;
10914         if(!this.modified){
10915             this.modified = {};
10916         }
10917         if(typeof this.modified[name] == 'undefined'){
10918             this.modified[name] = this.data[name];
10919         }
10920         this.data[name] = value;
10921         if(!this.editing && this.store){
10922             this.store.afterEdit(this);
10923         }       
10924     },
10925
10926     /**
10927      * Get the value of the named field.
10928      * @param {String} name The name of the field to get the value of.
10929      * @return {Object} The value of the field.
10930      */
10931     get : function(name){
10932         return this.data[name]; 
10933     },
10934
10935     // private
10936     beginEdit : function(){
10937         this.editing = true;
10938         this.modified = {}; 
10939     },
10940
10941     // private
10942     cancelEdit : function(){
10943         this.editing = false;
10944         delete this.modified;
10945     },
10946
10947     // private
10948     endEdit : function(){
10949         this.editing = false;
10950         if(this.dirty && this.store){
10951             this.store.afterEdit(this);
10952         }
10953     },
10954
10955     /**
10956      * Usually called by the {@link Roo.data.Store} which owns the Record.
10957      * Rejects all changes made to the Record since either creation, or the last commit operation.
10958      * Modified fields are reverted to their original values.
10959      * <p>
10960      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10961      * of reject operations.
10962      */
10963     reject : function(){
10964         var m = this.modified;
10965         for(var n in m){
10966             if(typeof m[n] != "function"){
10967                 this.data[n] = m[n];
10968             }
10969         }
10970         this.dirty = false;
10971         delete this.modified;
10972         this.editing = false;
10973         if(this.store){
10974             this.store.afterReject(this);
10975         }
10976     },
10977
10978     /**
10979      * Usually called by the {@link Roo.data.Store} which owns the Record.
10980      * Commits all changes made to the Record since either creation, or the last commit operation.
10981      * <p>
10982      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10983      * of commit operations.
10984      */
10985     commit : function(){
10986         this.dirty = false;
10987         delete this.modified;
10988         this.editing = false;
10989         if(this.store){
10990             this.store.afterCommit(this);
10991         }
10992     },
10993
10994     // private
10995     hasError : function(){
10996         return this.error != null;
10997     },
10998
10999     // private
11000     clearError : function(){
11001         this.error = null;
11002     },
11003
11004     /**
11005      * Creates a copy of this record.
11006      * @param {String} id (optional) A new record id if you don't want to use this record's id
11007      * @return {Record}
11008      */
11009     copy : function(newId) {
11010         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11011     }
11012 };/*
11013  * Based on:
11014  * Ext JS Library 1.1.1
11015  * Copyright(c) 2006-2007, Ext JS, LLC.
11016  *
11017  * Originally Released Under LGPL - original licence link has changed is not relivant.
11018  *
11019  * Fork - LGPL
11020  * <script type="text/javascript">
11021  */
11022
11023
11024
11025 /**
11026  * @class Roo.data.Store
11027  * @extends Roo.util.Observable
11028  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11029  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11030  * <p>
11031  * 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
11032  * has no knowledge of the format of the data returned by the Proxy.<br>
11033  * <p>
11034  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11035  * instances from the data object. These records are cached and made available through accessor functions.
11036  * @constructor
11037  * Creates a new Store.
11038  * @param {Object} config A config object containing the objects needed for the Store to access data,
11039  * and read the data into Records.
11040  */
11041 Roo.data.Store = function(config){
11042     this.data = new Roo.util.MixedCollection(false);
11043     this.data.getKey = function(o){
11044         return o.id;
11045     };
11046     this.baseParams = {};
11047     // private
11048     this.paramNames = {
11049         "start" : "start",
11050         "limit" : "limit",
11051         "sort" : "sort",
11052         "dir" : "dir",
11053         "multisort" : "_multisort"
11054     };
11055
11056     if(config && config.data){
11057         this.inlineData = config.data;
11058         delete config.data;
11059     }
11060
11061     Roo.apply(this, config);
11062     
11063     if(this.reader){ // reader passed
11064         this.reader = Roo.factory(this.reader, Roo.data);
11065         this.reader.xmodule = this.xmodule || false;
11066         if(!this.recordType){
11067             this.recordType = this.reader.recordType;
11068         }
11069         if(this.reader.onMetaChange){
11070             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11071         }
11072     }
11073
11074     if(this.recordType){
11075         this.fields = this.recordType.prototype.fields;
11076     }
11077     this.modified = [];
11078
11079     this.addEvents({
11080         /**
11081          * @event datachanged
11082          * Fires when the data cache has changed, and a widget which is using this Store
11083          * as a Record cache should refresh its view.
11084          * @param {Store} this
11085          */
11086         datachanged : true,
11087         /**
11088          * @event metachange
11089          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11090          * @param {Store} this
11091          * @param {Object} meta The JSON metadata
11092          */
11093         metachange : true,
11094         /**
11095          * @event add
11096          * Fires when Records have been added to the Store
11097          * @param {Store} this
11098          * @param {Roo.data.Record[]} records The array of Records added
11099          * @param {Number} index The index at which the record(s) were added
11100          */
11101         add : true,
11102         /**
11103          * @event remove
11104          * Fires when a Record has been removed from the Store
11105          * @param {Store} this
11106          * @param {Roo.data.Record} record The Record that was removed
11107          * @param {Number} index The index at which the record was removed
11108          */
11109         remove : true,
11110         /**
11111          * @event update
11112          * Fires when a Record has been updated
11113          * @param {Store} this
11114          * @param {Roo.data.Record} record The Record that was updated
11115          * @param {String} operation The update operation being performed.  Value may be one of:
11116          * <pre><code>
11117  Roo.data.Record.EDIT
11118  Roo.data.Record.REJECT
11119  Roo.data.Record.COMMIT
11120          * </code></pre>
11121          */
11122         update : true,
11123         /**
11124          * @event clear
11125          * Fires when the data cache has been cleared.
11126          * @param {Store} this
11127          */
11128         clear : true,
11129         /**
11130          * @event beforeload
11131          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11132          * the load action will be canceled.
11133          * @param {Store} this
11134          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11135          */
11136         beforeload : true,
11137         /**
11138          * @event beforeloadadd
11139          * Fires after a new set of Records has been loaded.
11140          * @param {Store} this
11141          * @param {Roo.data.Record[]} records The Records that were loaded
11142          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11143          */
11144         beforeloadadd : true,
11145         /**
11146          * @event load
11147          * Fires after a new set of Records has been loaded, before they are added to the store.
11148          * @param {Store} this
11149          * @param {Roo.data.Record[]} records The Records that were loaded
11150          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11151          * @params {Object} return from reader
11152          */
11153         load : true,
11154         /**
11155          * @event loadexception
11156          * Fires if an exception occurs in the Proxy during loading.
11157          * Called with the signature of the Proxy's "loadexception" event.
11158          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11159          * 
11160          * @param {Proxy} 
11161          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11162          * @param {Object} load options 
11163          * @param {Object} jsonData from your request (normally this contains the Exception)
11164          */
11165         loadexception : true
11166     });
11167     
11168     if(this.proxy){
11169         this.proxy = Roo.factory(this.proxy, Roo.data);
11170         this.proxy.xmodule = this.xmodule || false;
11171         this.relayEvents(this.proxy,  ["loadexception"]);
11172     }
11173     this.sortToggle = {};
11174     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11175
11176     Roo.data.Store.superclass.constructor.call(this);
11177
11178     if(this.inlineData){
11179         this.loadData(this.inlineData);
11180         delete this.inlineData;
11181     }
11182 };
11183
11184 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11185      /**
11186     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11187     * without a remote query - used by combo/forms at present.
11188     */
11189     
11190     /**
11191     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11192     */
11193     /**
11194     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11195     */
11196     /**
11197     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11198     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11199     */
11200     /**
11201     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11202     * on any HTTP request
11203     */
11204     /**
11205     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11206     */
11207     /**
11208     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11209     */
11210     multiSort: false,
11211     /**
11212     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11213     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11214     */
11215     remoteSort : false,
11216
11217     /**
11218     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11219      * loaded or when a record is removed. (defaults to false).
11220     */
11221     pruneModifiedRecords : false,
11222
11223     // private
11224     lastOptions : null,
11225
11226     /**
11227      * Add Records to the Store and fires the add event.
11228      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11229      */
11230     add : function(records){
11231         records = [].concat(records);
11232         for(var i = 0, len = records.length; i < len; i++){
11233             records[i].join(this);
11234         }
11235         var index = this.data.length;
11236         this.data.addAll(records);
11237         this.fireEvent("add", this, records, index);
11238     },
11239
11240     /**
11241      * Remove a Record from the Store and fires the remove event.
11242      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11243      */
11244     remove : function(record){
11245         var index = this.data.indexOf(record);
11246         this.data.removeAt(index);
11247  
11248         if(this.pruneModifiedRecords){
11249             this.modified.remove(record);
11250         }
11251         this.fireEvent("remove", this, record, index);
11252     },
11253
11254     /**
11255      * Remove all Records from the Store and fires the clear event.
11256      */
11257     removeAll : function(){
11258         this.data.clear();
11259         if(this.pruneModifiedRecords){
11260             this.modified = [];
11261         }
11262         this.fireEvent("clear", this);
11263     },
11264
11265     /**
11266      * Inserts Records to the Store at the given index and fires the add event.
11267      * @param {Number} index The start index at which to insert the passed Records.
11268      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11269      */
11270     insert : function(index, records){
11271         records = [].concat(records);
11272         for(var i = 0, len = records.length; i < len; i++){
11273             this.data.insert(index, records[i]);
11274             records[i].join(this);
11275         }
11276         this.fireEvent("add", this, records, index);
11277     },
11278
11279     /**
11280      * Get the index within the cache of the passed Record.
11281      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11282      * @return {Number} The index of the passed Record. Returns -1 if not found.
11283      */
11284     indexOf : function(record){
11285         return this.data.indexOf(record);
11286     },
11287
11288     /**
11289      * Get the index within the cache of the Record with the passed id.
11290      * @param {String} id The id of the Record to find.
11291      * @return {Number} The index of the Record. Returns -1 if not found.
11292      */
11293     indexOfId : function(id){
11294         return this.data.indexOfKey(id);
11295     },
11296
11297     /**
11298      * Get the Record with the specified id.
11299      * @param {String} id The id of the Record to find.
11300      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11301      */
11302     getById : function(id){
11303         return this.data.key(id);
11304     },
11305
11306     /**
11307      * Get the Record at the specified index.
11308      * @param {Number} index The index of the Record to find.
11309      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11310      */
11311     getAt : function(index){
11312         return this.data.itemAt(index);
11313     },
11314
11315     /**
11316      * Returns a range of Records between specified indices.
11317      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11318      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11319      * @return {Roo.data.Record[]} An array of Records
11320      */
11321     getRange : function(start, end){
11322         return this.data.getRange(start, end);
11323     },
11324
11325     // private
11326     storeOptions : function(o){
11327         o = Roo.apply({}, o);
11328         delete o.callback;
11329         delete o.scope;
11330         this.lastOptions = o;
11331     },
11332
11333     /**
11334      * Loads the Record cache from the configured Proxy using the configured Reader.
11335      * <p>
11336      * If using remote paging, then the first load call must specify the <em>start</em>
11337      * and <em>limit</em> properties in the options.params property to establish the initial
11338      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11339      * <p>
11340      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11341      * and this call will return before the new data has been loaded. Perform any post-processing
11342      * in a callback function, or in a "load" event handler.</strong>
11343      * <p>
11344      * @param {Object} options An object containing properties which control loading options:<ul>
11345      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11346      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11347      * passed the following arguments:<ul>
11348      * <li>r : Roo.data.Record[]</li>
11349      * <li>options: Options object from the load call</li>
11350      * <li>success: Boolean success indicator</li></ul></li>
11351      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11352      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11353      * </ul>
11354      */
11355     load : function(options){
11356         options = options || {};
11357         if(this.fireEvent("beforeload", this, options) !== false){
11358             this.storeOptions(options);
11359             var p = Roo.apply(options.params || {}, this.baseParams);
11360             // if meta was not loaded from remote source.. try requesting it.
11361             if (!this.reader.metaFromRemote) {
11362                 p._requestMeta = 1;
11363             }
11364             if(this.sortInfo && this.remoteSort){
11365                 var pn = this.paramNames;
11366                 p[pn["sort"]] = this.sortInfo.field;
11367                 p[pn["dir"]] = this.sortInfo.direction;
11368             }
11369             if (this.multiSort) {
11370                 var pn = this.paramNames;
11371                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11372             }
11373             
11374             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11375         }
11376     },
11377
11378     /**
11379      * Reloads the Record cache from the configured Proxy using the configured Reader and
11380      * the options from the last load operation performed.
11381      * @param {Object} options (optional) An object containing properties which may override the options
11382      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11383      * the most recently used options are reused).
11384      */
11385     reload : function(options){
11386         this.load(Roo.applyIf(options||{}, this.lastOptions));
11387     },
11388
11389     // private
11390     // Called as a callback by the Reader during a load operation.
11391     loadRecords : function(o, options, success){
11392         if(!o || success === false){
11393             if(success !== false){
11394                 this.fireEvent("load", this, [], options, o);
11395             }
11396             if(options.callback){
11397                 options.callback.call(options.scope || this, [], options, false);
11398             }
11399             return;
11400         }
11401         // if data returned failure - throw an exception.
11402         if (o.success === false) {
11403             // show a message if no listener is registered.
11404             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11405                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11406             }
11407             // loadmask wil be hooked into this..
11408             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11409             return;
11410         }
11411         var r = o.records, t = o.totalRecords || r.length;
11412         
11413         this.fireEvent("beforeloadadd", this, r, options, o);
11414         
11415         if(!options || options.add !== true){
11416             if(this.pruneModifiedRecords){
11417                 this.modified = [];
11418             }
11419             for(var i = 0, len = r.length; i < len; i++){
11420                 r[i].join(this);
11421             }
11422             if(this.snapshot){
11423                 this.data = this.snapshot;
11424                 delete this.snapshot;
11425             }
11426             this.data.clear();
11427             this.data.addAll(r);
11428             this.totalLength = t;
11429             this.applySort();
11430             this.fireEvent("datachanged", this);
11431         }else{
11432             this.totalLength = Math.max(t, this.data.length+r.length);
11433             this.add(r);
11434         }
11435         
11436         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11437                 
11438             var e = new Roo.data.Record({});
11439
11440             e.set(this.parent.displayField, this.parent.emptyTitle);
11441             e.set(this.parent.valueField, '');
11442
11443             this.insert(0, e);
11444         }
11445             
11446         this.fireEvent("load", this, r, options, o);
11447         if(options.callback){
11448             options.callback.call(options.scope || this, r, options, true);
11449         }
11450     },
11451
11452
11453     /**
11454      * Loads data from a passed data block. A Reader which understands the format of the data
11455      * must have been configured in the constructor.
11456      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11457      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11458      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11459      */
11460     loadData : function(o, append){
11461         var r = this.reader.readRecords(o);
11462         this.loadRecords(r, {add: append}, true);
11463     },
11464
11465     /**
11466      * Gets the number of cached records.
11467      * <p>
11468      * <em>If using paging, this may not be the total size of the dataset. If the data object
11469      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11470      * the data set size</em>
11471      */
11472     getCount : function(){
11473         return this.data.length || 0;
11474     },
11475
11476     /**
11477      * Gets the total number of records in the dataset as returned by the server.
11478      * <p>
11479      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11480      * the dataset size</em>
11481      */
11482     getTotalCount : function(){
11483         return this.totalLength || 0;
11484     },
11485
11486     /**
11487      * Returns the sort state of the Store as an object with two properties:
11488      * <pre><code>
11489  field {String} The name of the field by which the Records are sorted
11490  direction {String} The sort order, "ASC" or "DESC"
11491      * </code></pre>
11492      */
11493     getSortState : function(){
11494         return this.sortInfo;
11495     },
11496
11497     // private
11498     applySort : function(){
11499         if(this.sortInfo && !this.remoteSort){
11500             var s = this.sortInfo, f = s.field;
11501             var st = this.fields.get(f).sortType;
11502             var fn = function(r1, r2){
11503                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11504                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11505             };
11506             this.data.sort(s.direction, fn);
11507             if(this.snapshot && this.snapshot != this.data){
11508                 this.snapshot.sort(s.direction, fn);
11509             }
11510         }
11511     },
11512
11513     /**
11514      * Sets the default sort column and order to be used by the next load operation.
11515      * @param {String} fieldName The name of the field to sort by.
11516      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11517      */
11518     setDefaultSort : function(field, dir){
11519         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11520     },
11521
11522     /**
11523      * Sort the Records.
11524      * If remote sorting is used, the sort is performed on the server, and the cache is
11525      * reloaded. If local sorting is used, the cache is sorted internally.
11526      * @param {String} fieldName The name of the field to sort by.
11527      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11528      */
11529     sort : function(fieldName, dir){
11530         var f = this.fields.get(fieldName);
11531         if(!dir){
11532             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11533             
11534             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11535                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11536             }else{
11537                 dir = f.sortDir;
11538             }
11539         }
11540         this.sortToggle[f.name] = dir;
11541         this.sortInfo = {field: f.name, direction: dir};
11542         if(!this.remoteSort){
11543             this.applySort();
11544             this.fireEvent("datachanged", this);
11545         }else{
11546             this.load(this.lastOptions);
11547         }
11548     },
11549
11550     /**
11551      * Calls the specified function for each of the Records in the cache.
11552      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11553      * Returning <em>false</em> aborts and exits the iteration.
11554      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11555      */
11556     each : function(fn, scope){
11557         this.data.each(fn, scope);
11558     },
11559
11560     /**
11561      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11562      * (e.g., during paging).
11563      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11564      */
11565     getModifiedRecords : function(){
11566         return this.modified;
11567     },
11568
11569     // private
11570     createFilterFn : function(property, value, anyMatch){
11571         if(!value.exec){ // not a regex
11572             value = String(value);
11573             if(value.length == 0){
11574                 return false;
11575             }
11576             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11577         }
11578         return function(r){
11579             return value.test(r.data[property]);
11580         };
11581     },
11582
11583     /**
11584      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11585      * @param {String} property A field on your records
11586      * @param {Number} start The record index to start at (defaults to 0)
11587      * @param {Number} end The last record index to include (defaults to length - 1)
11588      * @return {Number} The sum
11589      */
11590     sum : function(property, start, end){
11591         var rs = this.data.items, v = 0;
11592         start = start || 0;
11593         end = (end || end === 0) ? end : rs.length-1;
11594
11595         for(var i = start; i <= end; i++){
11596             v += (rs[i].data[property] || 0);
11597         }
11598         return v;
11599     },
11600
11601     /**
11602      * Filter the records by a specified property.
11603      * @param {String} field A field on your records
11604      * @param {String/RegExp} value Either a string that the field
11605      * should start with or a RegExp to test against the field
11606      * @param {Boolean} anyMatch True to match any part not just the beginning
11607      */
11608     filter : function(property, value, anyMatch){
11609         var fn = this.createFilterFn(property, value, anyMatch);
11610         return fn ? this.filterBy(fn) : this.clearFilter();
11611     },
11612
11613     /**
11614      * Filter by a function. The specified function will be called with each
11615      * record in this data source. If the function returns true the record is included,
11616      * otherwise it is filtered.
11617      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11618      * @param {Object} scope (optional) The scope of the function (defaults to this)
11619      */
11620     filterBy : function(fn, scope){
11621         this.snapshot = this.snapshot || this.data;
11622         this.data = this.queryBy(fn, scope||this);
11623         this.fireEvent("datachanged", this);
11624     },
11625
11626     /**
11627      * Query the records by a specified property.
11628      * @param {String} field A field on your records
11629      * @param {String/RegExp} value Either a string that the field
11630      * should start with or a RegExp to test against the field
11631      * @param {Boolean} anyMatch True to match any part not just the beginning
11632      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11633      */
11634     query : function(property, value, anyMatch){
11635         var fn = this.createFilterFn(property, value, anyMatch);
11636         return fn ? this.queryBy(fn) : this.data.clone();
11637     },
11638
11639     /**
11640      * Query by a function. The specified function will be called with each
11641      * record in this data source. If the function returns true the record is included
11642      * in the results.
11643      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11644      * @param {Object} scope (optional) The scope of the function (defaults to this)
11645       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11646      **/
11647     queryBy : function(fn, scope){
11648         var data = this.snapshot || this.data;
11649         return data.filterBy(fn, scope||this);
11650     },
11651
11652     /**
11653      * Collects unique values for a particular dataIndex from this store.
11654      * @param {String} dataIndex The property to collect
11655      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11656      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11657      * @return {Array} An array of the unique values
11658      **/
11659     collect : function(dataIndex, allowNull, bypassFilter){
11660         var d = (bypassFilter === true && this.snapshot) ?
11661                 this.snapshot.items : this.data.items;
11662         var v, sv, r = [], l = {};
11663         for(var i = 0, len = d.length; i < len; i++){
11664             v = d[i].data[dataIndex];
11665             sv = String(v);
11666             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11667                 l[sv] = true;
11668                 r[r.length] = v;
11669             }
11670         }
11671         return r;
11672     },
11673
11674     /**
11675      * Revert to a view of the Record cache with no filtering applied.
11676      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11677      */
11678     clearFilter : function(suppressEvent){
11679         if(this.snapshot && this.snapshot != this.data){
11680             this.data = this.snapshot;
11681             delete this.snapshot;
11682             if(suppressEvent !== true){
11683                 this.fireEvent("datachanged", this);
11684             }
11685         }
11686     },
11687
11688     // private
11689     afterEdit : function(record){
11690         if(this.modified.indexOf(record) == -1){
11691             this.modified.push(record);
11692         }
11693         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11694     },
11695     
11696     // private
11697     afterReject : function(record){
11698         this.modified.remove(record);
11699         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11700     },
11701
11702     // private
11703     afterCommit : function(record){
11704         this.modified.remove(record);
11705         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11706     },
11707
11708     /**
11709      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11710      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11711      */
11712     commitChanges : function(){
11713         var m = this.modified.slice(0);
11714         this.modified = [];
11715         for(var i = 0, len = m.length; i < len; i++){
11716             m[i].commit();
11717         }
11718     },
11719
11720     /**
11721      * Cancel outstanding changes on all changed records.
11722      */
11723     rejectChanges : function(){
11724         var m = this.modified.slice(0);
11725         this.modified = [];
11726         for(var i = 0, len = m.length; i < len; i++){
11727             m[i].reject();
11728         }
11729     },
11730
11731     onMetaChange : function(meta, rtype, o){
11732         this.recordType = rtype;
11733         this.fields = rtype.prototype.fields;
11734         delete this.snapshot;
11735         this.sortInfo = meta.sortInfo || this.sortInfo;
11736         this.modified = [];
11737         this.fireEvent('metachange', this, this.reader.meta);
11738     },
11739     
11740     moveIndex : function(data, type)
11741     {
11742         var index = this.indexOf(data);
11743         
11744         var newIndex = index + type;
11745         
11746         this.remove(data);
11747         
11748         this.insert(newIndex, data);
11749         
11750     }
11751 });/*
11752  * Based on:
11753  * Ext JS Library 1.1.1
11754  * Copyright(c) 2006-2007, Ext JS, LLC.
11755  *
11756  * Originally Released Under LGPL - original licence link has changed is not relivant.
11757  *
11758  * Fork - LGPL
11759  * <script type="text/javascript">
11760  */
11761
11762 /**
11763  * @class Roo.data.SimpleStore
11764  * @extends Roo.data.Store
11765  * Small helper class to make creating Stores from Array data easier.
11766  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11767  * @cfg {Array} fields An array of field definition objects, or field name strings.
11768  * @cfg {Array} data The multi-dimensional array of data
11769  * @constructor
11770  * @param {Object} config
11771  */
11772 Roo.data.SimpleStore = function(config){
11773     Roo.data.SimpleStore.superclass.constructor.call(this, {
11774         isLocal : true,
11775         reader: new Roo.data.ArrayReader({
11776                 id: config.id
11777             },
11778             Roo.data.Record.create(config.fields)
11779         ),
11780         proxy : new Roo.data.MemoryProxy(config.data)
11781     });
11782     this.load();
11783 };
11784 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11785  * Based on:
11786  * Ext JS Library 1.1.1
11787  * Copyright(c) 2006-2007, Ext JS, LLC.
11788  *
11789  * Originally Released Under LGPL - original licence link has changed is not relivant.
11790  *
11791  * Fork - LGPL
11792  * <script type="text/javascript">
11793  */
11794
11795 /**
11796 /**
11797  * @extends Roo.data.Store
11798  * @class Roo.data.JsonStore
11799  * Small helper class to make creating Stores for JSON data easier. <br/>
11800 <pre><code>
11801 var store = new Roo.data.JsonStore({
11802     url: 'get-images.php',
11803     root: 'images',
11804     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11805 });
11806 </code></pre>
11807  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11808  * JsonReader and HttpProxy (unless inline data is provided).</b>
11809  * @cfg {Array} fields An array of field definition objects, or field name strings.
11810  * @constructor
11811  * @param {Object} config
11812  */
11813 Roo.data.JsonStore = function(c){
11814     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11815         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11816         reader: new Roo.data.JsonReader(c, c.fields)
11817     }));
11818 };
11819 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11820  * Based on:
11821  * Ext JS Library 1.1.1
11822  * Copyright(c) 2006-2007, Ext JS, LLC.
11823  *
11824  * Originally Released Under LGPL - original licence link has changed is not relivant.
11825  *
11826  * Fork - LGPL
11827  * <script type="text/javascript">
11828  */
11829
11830  
11831 Roo.data.Field = function(config){
11832     if(typeof config == "string"){
11833         config = {name: config};
11834     }
11835     Roo.apply(this, config);
11836     
11837     if(!this.type){
11838         this.type = "auto";
11839     }
11840     
11841     var st = Roo.data.SortTypes;
11842     // named sortTypes are supported, here we look them up
11843     if(typeof this.sortType == "string"){
11844         this.sortType = st[this.sortType];
11845     }
11846     
11847     // set default sortType for strings and dates
11848     if(!this.sortType){
11849         switch(this.type){
11850             case "string":
11851                 this.sortType = st.asUCString;
11852                 break;
11853             case "date":
11854                 this.sortType = st.asDate;
11855                 break;
11856             default:
11857                 this.sortType = st.none;
11858         }
11859     }
11860
11861     // define once
11862     var stripRe = /[\$,%]/g;
11863
11864     // prebuilt conversion function for this field, instead of
11865     // switching every time we're reading a value
11866     if(!this.convert){
11867         var cv, dateFormat = this.dateFormat;
11868         switch(this.type){
11869             case "":
11870             case "auto":
11871             case undefined:
11872                 cv = function(v){ return v; };
11873                 break;
11874             case "string":
11875                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11876                 break;
11877             case "int":
11878                 cv = function(v){
11879                     return v !== undefined && v !== null && v !== '' ?
11880                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11881                     };
11882                 break;
11883             case "float":
11884                 cv = function(v){
11885                     return v !== undefined && v !== null && v !== '' ?
11886                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11887                     };
11888                 break;
11889             case "bool":
11890             case "boolean":
11891                 cv = function(v){ return v === true || v === "true" || v == 1; };
11892                 break;
11893             case "date":
11894                 cv = function(v){
11895                     if(!v){
11896                         return '';
11897                     }
11898                     if(v instanceof Date){
11899                         return v;
11900                     }
11901                     if(dateFormat){
11902                         if(dateFormat == "timestamp"){
11903                             return new Date(v*1000);
11904                         }
11905                         return Date.parseDate(v, dateFormat);
11906                     }
11907                     var parsed = Date.parse(v);
11908                     return parsed ? new Date(parsed) : null;
11909                 };
11910              break;
11911             
11912         }
11913         this.convert = cv;
11914     }
11915 };
11916
11917 Roo.data.Field.prototype = {
11918     dateFormat: null,
11919     defaultValue: "",
11920     mapping: null,
11921     sortType : null,
11922     sortDir : "ASC"
11923 };/*
11924  * Based on:
11925  * Ext JS Library 1.1.1
11926  * Copyright(c) 2006-2007, Ext JS, LLC.
11927  *
11928  * Originally Released Under LGPL - original licence link has changed is not relivant.
11929  *
11930  * Fork - LGPL
11931  * <script type="text/javascript">
11932  */
11933  
11934 // Base class for reading structured data from a data source.  This class is intended to be
11935 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11936
11937 /**
11938  * @class Roo.data.DataReader
11939  * Base class for reading structured data from a data source.  This class is intended to be
11940  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11941  */
11942
11943 Roo.data.DataReader = function(meta, recordType){
11944     
11945     this.meta = meta;
11946     
11947     this.recordType = recordType instanceof Array ? 
11948         Roo.data.Record.create(recordType) : recordType;
11949 };
11950
11951 Roo.data.DataReader.prototype = {
11952      /**
11953      * Create an empty record
11954      * @param {Object} data (optional) - overlay some values
11955      * @return {Roo.data.Record} record created.
11956      */
11957     newRow :  function(d) {
11958         var da =  {};
11959         this.recordType.prototype.fields.each(function(c) {
11960             switch( c.type) {
11961                 case 'int' : da[c.name] = 0; break;
11962                 case 'date' : da[c.name] = new Date(); break;
11963                 case 'float' : da[c.name] = 0.0; break;
11964                 case 'boolean' : da[c.name] = false; break;
11965                 default : da[c.name] = ""; break;
11966             }
11967             
11968         });
11969         return new this.recordType(Roo.apply(da, d));
11970     }
11971     
11972 };/*
11973  * Based on:
11974  * Ext JS Library 1.1.1
11975  * Copyright(c) 2006-2007, Ext JS, LLC.
11976  *
11977  * Originally Released Under LGPL - original licence link has changed is not relivant.
11978  *
11979  * Fork - LGPL
11980  * <script type="text/javascript">
11981  */
11982
11983 /**
11984  * @class Roo.data.DataProxy
11985  * @extends Roo.data.Observable
11986  * This class is an abstract base class for implementations which provide retrieval of
11987  * unformatted data objects.<br>
11988  * <p>
11989  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11990  * (of the appropriate type which knows how to parse the data object) to provide a block of
11991  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11992  * <p>
11993  * Custom implementations must implement the load method as described in
11994  * {@link Roo.data.HttpProxy#load}.
11995  */
11996 Roo.data.DataProxy = function(){
11997     this.addEvents({
11998         /**
11999          * @event beforeload
12000          * Fires before a network request is made to retrieve a data object.
12001          * @param {Object} This DataProxy object.
12002          * @param {Object} params The params parameter to the load function.
12003          */
12004         beforeload : true,
12005         /**
12006          * @event load
12007          * Fires before the load method's callback is called.
12008          * @param {Object} This DataProxy object.
12009          * @param {Object} o The data object.
12010          * @param {Object} arg The callback argument object passed to the load function.
12011          */
12012         load : true,
12013         /**
12014          * @event loadexception
12015          * Fires if an Exception occurs during data retrieval.
12016          * @param {Object} This DataProxy object.
12017          * @param {Object} o The data object.
12018          * @param {Object} arg The callback argument object passed to the load function.
12019          * @param {Object} e The Exception.
12020          */
12021         loadexception : true
12022     });
12023     Roo.data.DataProxy.superclass.constructor.call(this);
12024 };
12025
12026 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12027
12028     /**
12029      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12030      */
12031 /*
12032  * Based on:
12033  * Ext JS Library 1.1.1
12034  * Copyright(c) 2006-2007, Ext JS, LLC.
12035  *
12036  * Originally Released Under LGPL - original licence link has changed is not relivant.
12037  *
12038  * Fork - LGPL
12039  * <script type="text/javascript">
12040  */
12041 /**
12042  * @class Roo.data.MemoryProxy
12043  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12044  * to the Reader when its load method is called.
12045  * @constructor
12046  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12047  */
12048 Roo.data.MemoryProxy = function(data){
12049     if (data.data) {
12050         data = data.data;
12051     }
12052     Roo.data.MemoryProxy.superclass.constructor.call(this);
12053     this.data = data;
12054 };
12055
12056 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12057     
12058     /**
12059      * Load data from the requested source (in this case an in-memory
12060      * data object passed to the constructor), read the data object into
12061      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12062      * process that block using the passed callback.
12063      * @param {Object} params This parameter is not used by the MemoryProxy class.
12064      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12065      * object into a block of Roo.data.Records.
12066      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12067      * The function must be passed <ul>
12068      * <li>The Record block object</li>
12069      * <li>The "arg" argument from the load function</li>
12070      * <li>A boolean success indicator</li>
12071      * </ul>
12072      * @param {Object} scope The scope in which to call the callback
12073      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12074      */
12075     load : function(params, reader, callback, scope, arg){
12076         params = params || {};
12077         var result;
12078         try {
12079             result = reader.readRecords(this.data);
12080         }catch(e){
12081             this.fireEvent("loadexception", this, arg, null, e);
12082             callback.call(scope, null, arg, false);
12083             return;
12084         }
12085         callback.call(scope, result, arg, true);
12086     },
12087     
12088     // private
12089     update : function(params, records){
12090         
12091     }
12092 });/*
12093  * Based on:
12094  * Ext JS Library 1.1.1
12095  * Copyright(c) 2006-2007, Ext JS, LLC.
12096  *
12097  * Originally Released Under LGPL - original licence link has changed is not relivant.
12098  *
12099  * Fork - LGPL
12100  * <script type="text/javascript">
12101  */
12102 /**
12103  * @class Roo.data.HttpProxy
12104  * @extends Roo.data.DataProxy
12105  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12106  * configured to reference a certain URL.<br><br>
12107  * <p>
12108  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12109  * from which the running page was served.<br><br>
12110  * <p>
12111  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12112  * <p>
12113  * Be aware that to enable the browser to parse an XML document, the server must set
12114  * the Content-Type header in the HTTP response to "text/xml".
12115  * @constructor
12116  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12117  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12118  * will be used to make the request.
12119  */
12120 Roo.data.HttpProxy = function(conn){
12121     Roo.data.HttpProxy.superclass.constructor.call(this);
12122     // is conn a conn config or a real conn?
12123     this.conn = conn;
12124     this.useAjax = !conn || !conn.events;
12125   
12126 };
12127
12128 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12129     // thse are take from connection...
12130     
12131     /**
12132      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12133      */
12134     /**
12135      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12136      * extra parameters to each request made by this object. (defaults to undefined)
12137      */
12138     /**
12139      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12140      *  to each request made by this object. (defaults to undefined)
12141      */
12142     /**
12143      * @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)
12144      */
12145     /**
12146      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12147      */
12148      /**
12149      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12150      * @type Boolean
12151      */
12152   
12153
12154     /**
12155      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12156      * @type Boolean
12157      */
12158     /**
12159      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12160      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12161      * a finer-grained basis than the DataProxy events.
12162      */
12163     getConnection : function(){
12164         return this.useAjax ? Roo.Ajax : this.conn;
12165     },
12166
12167     /**
12168      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12169      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12170      * process that block using the passed callback.
12171      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12172      * for the request to the remote server.
12173      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12174      * object into a block of Roo.data.Records.
12175      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12176      * The function must be passed <ul>
12177      * <li>The Record block object</li>
12178      * <li>The "arg" argument from the load function</li>
12179      * <li>A boolean success indicator</li>
12180      * </ul>
12181      * @param {Object} scope The scope in which to call the callback
12182      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12183      */
12184     load : function(params, reader, callback, scope, arg){
12185         if(this.fireEvent("beforeload", this, params) !== false){
12186             var  o = {
12187                 params : params || {},
12188                 request: {
12189                     callback : callback,
12190                     scope : scope,
12191                     arg : arg
12192                 },
12193                 reader: reader,
12194                 callback : this.loadResponse,
12195                 scope: this
12196             };
12197             if(this.useAjax){
12198                 Roo.applyIf(o, this.conn);
12199                 if(this.activeRequest){
12200                     Roo.Ajax.abort(this.activeRequest);
12201                 }
12202                 this.activeRequest = Roo.Ajax.request(o);
12203             }else{
12204                 this.conn.request(o);
12205             }
12206         }else{
12207             callback.call(scope||this, null, arg, false);
12208         }
12209     },
12210
12211     // private
12212     loadResponse : function(o, success, response){
12213         delete this.activeRequest;
12214         if(!success){
12215             this.fireEvent("loadexception", this, o, response);
12216             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12217             return;
12218         }
12219         var result;
12220         try {
12221             result = o.reader.read(response);
12222         }catch(e){
12223             this.fireEvent("loadexception", this, o, response, e);
12224             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12225             return;
12226         }
12227         
12228         this.fireEvent("load", this, o, o.request.arg);
12229         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12230     },
12231
12232     // private
12233     update : function(dataSet){
12234
12235     },
12236
12237     // private
12238     updateResponse : function(dataSet){
12239
12240     }
12241 });/*
12242  * Based on:
12243  * Ext JS Library 1.1.1
12244  * Copyright(c) 2006-2007, Ext JS, LLC.
12245  *
12246  * Originally Released Under LGPL - original licence link has changed is not relivant.
12247  *
12248  * Fork - LGPL
12249  * <script type="text/javascript">
12250  */
12251
12252 /**
12253  * @class Roo.data.ScriptTagProxy
12254  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12255  * other than the originating domain of the running page.<br><br>
12256  * <p>
12257  * <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
12258  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12259  * <p>
12260  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12261  * source code that is used as the source inside a &lt;script> tag.<br><br>
12262  * <p>
12263  * In order for the browser to process the returned data, the server must wrap the data object
12264  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12265  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12266  * depending on whether the callback name was passed:
12267  * <p>
12268  * <pre><code>
12269 boolean scriptTag = false;
12270 String cb = request.getParameter("callback");
12271 if (cb != null) {
12272     scriptTag = true;
12273     response.setContentType("text/javascript");
12274 } else {
12275     response.setContentType("application/x-json");
12276 }
12277 Writer out = response.getWriter();
12278 if (scriptTag) {
12279     out.write(cb + "(");
12280 }
12281 out.print(dataBlock.toJsonString());
12282 if (scriptTag) {
12283     out.write(");");
12284 }
12285 </pre></code>
12286  *
12287  * @constructor
12288  * @param {Object} config A configuration object.
12289  */
12290 Roo.data.ScriptTagProxy = function(config){
12291     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12292     Roo.apply(this, config);
12293     this.head = document.getElementsByTagName("head")[0];
12294 };
12295
12296 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12297
12298 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12299     /**
12300      * @cfg {String} url The URL from which to request the data object.
12301      */
12302     /**
12303      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12304      */
12305     timeout : 30000,
12306     /**
12307      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12308      * the server the name of the callback function set up by the load call to process the returned data object.
12309      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12310      * javascript output which calls this named function passing the data object as its only parameter.
12311      */
12312     callbackParam : "callback",
12313     /**
12314      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12315      * name to the request.
12316      */
12317     nocache : true,
12318
12319     /**
12320      * Load data from the configured URL, read the data object into
12321      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12322      * process that block using the passed callback.
12323      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12324      * for the request to the remote server.
12325      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12326      * object into a block of Roo.data.Records.
12327      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12328      * The function must be passed <ul>
12329      * <li>The Record block object</li>
12330      * <li>The "arg" argument from the load function</li>
12331      * <li>A boolean success indicator</li>
12332      * </ul>
12333      * @param {Object} scope The scope in which to call the callback
12334      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12335      */
12336     load : function(params, reader, callback, scope, arg){
12337         if(this.fireEvent("beforeload", this, params) !== false){
12338
12339             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12340
12341             var url = this.url;
12342             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12343             if(this.nocache){
12344                 url += "&_dc=" + (new Date().getTime());
12345             }
12346             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12347             var trans = {
12348                 id : transId,
12349                 cb : "stcCallback"+transId,
12350                 scriptId : "stcScript"+transId,
12351                 params : params,
12352                 arg : arg,
12353                 url : url,
12354                 callback : callback,
12355                 scope : scope,
12356                 reader : reader
12357             };
12358             var conn = this;
12359
12360             window[trans.cb] = function(o){
12361                 conn.handleResponse(o, trans);
12362             };
12363
12364             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12365
12366             if(this.autoAbort !== false){
12367                 this.abort();
12368             }
12369
12370             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12371
12372             var script = document.createElement("script");
12373             script.setAttribute("src", url);
12374             script.setAttribute("type", "text/javascript");
12375             script.setAttribute("id", trans.scriptId);
12376             this.head.appendChild(script);
12377
12378             this.trans = trans;
12379         }else{
12380             callback.call(scope||this, null, arg, false);
12381         }
12382     },
12383
12384     // private
12385     isLoading : function(){
12386         return this.trans ? true : false;
12387     },
12388
12389     /**
12390      * Abort the current server request.
12391      */
12392     abort : function(){
12393         if(this.isLoading()){
12394             this.destroyTrans(this.trans);
12395         }
12396     },
12397
12398     // private
12399     destroyTrans : function(trans, isLoaded){
12400         this.head.removeChild(document.getElementById(trans.scriptId));
12401         clearTimeout(trans.timeoutId);
12402         if(isLoaded){
12403             window[trans.cb] = undefined;
12404             try{
12405                 delete window[trans.cb];
12406             }catch(e){}
12407         }else{
12408             // if hasn't been loaded, wait for load to remove it to prevent script error
12409             window[trans.cb] = function(){
12410                 window[trans.cb] = undefined;
12411                 try{
12412                     delete window[trans.cb];
12413                 }catch(e){}
12414             };
12415         }
12416     },
12417
12418     // private
12419     handleResponse : function(o, trans){
12420         this.trans = false;
12421         this.destroyTrans(trans, true);
12422         var result;
12423         try {
12424             result = trans.reader.readRecords(o);
12425         }catch(e){
12426             this.fireEvent("loadexception", this, o, trans.arg, e);
12427             trans.callback.call(trans.scope||window, null, trans.arg, false);
12428             return;
12429         }
12430         this.fireEvent("load", this, o, trans.arg);
12431         trans.callback.call(trans.scope||window, result, trans.arg, true);
12432     },
12433
12434     // private
12435     handleFailure : function(trans){
12436         this.trans = false;
12437         this.destroyTrans(trans, false);
12438         this.fireEvent("loadexception", this, null, trans.arg);
12439         trans.callback.call(trans.scope||window, null, trans.arg, false);
12440     }
12441 });/*
12442  * Based on:
12443  * Ext JS Library 1.1.1
12444  * Copyright(c) 2006-2007, Ext JS, LLC.
12445  *
12446  * Originally Released Under LGPL - original licence link has changed is not relivant.
12447  *
12448  * Fork - LGPL
12449  * <script type="text/javascript">
12450  */
12451
12452 /**
12453  * @class Roo.data.JsonReader
12454  * @extends Roo.data.DataReader
12455  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12456  * based on mappings in a provided Roo.data.Record constructor.
12457  * 
12458  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12459  * in the reply previously. 
12460  * 
12461  * <p>
12462  * Example code:
12463  * <pre><code>
12464 var RecordDef = Roo.data.Record.create([
12465     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12466     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12467 ]);
12468 var myReader = new Roo.data.JsonReader({
12469     totalProperty: "results",    // The property which contains the total dataset size (optional)
12470     root: "rows",                // The property which contains an Array of row objects
12471     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12472 }, RecordDef);
12473 </code></pre>
12474  * <p>
12475  * This would consume a JSON file like this:
12476  * <pre><code>
12477 { 'results': 2, 'rows': [
12478     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12479     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12480 }
12481 </code></pre>
12482  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12483  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12484  * paged from the remote server.
12485  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12486  * @cfg {String} root name of the property which contains the Array of row objects.
12487  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12488  * @cfg {Array} fields Array of field definition objects
12489  * @constructor
12490  * Create a new JsonReader
12491  * @param {Object} meta Metadata configuration options
12492  * @param {Object} recordType Either an Array of field definition objects,
12493  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12494  */
12495 Roo.data.JsonReader = function(meta, recordType){
12496     
12497     meta = meta || {};
12498     // set some defaults:
12499     Roo.applyIf(meta, {
12500         totalProperty: 'total',
12501         successProperty : 'success',
12502         root : 'data',
12503         id : 'id'
12504     });
12505     
12506     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12507 };
12508 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12509     
12510     /**
12511      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12512      * Used by Store query builder to append _requestMeta to params.
12513      * 
12514      */
12515     metaFromRemote : false,
12516     /**
12517      * This method is only used by a DataProxy which has retrieved data from a remote server.
12518      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12519      * @return {Object} data A data block which is used by an Roo.data.Store object as
12520      * a cache of Roo.data.Records.
12521      */
12522     read : function(response){
12523         var json = response.responseText;
12524        
12525         var o = /* eval:var:o */ eval("("+json+")");
12526         if(!o) {
12527             throw {message: "JsonReader.read: Json object not found"};
12528         }
12529         
12530         if(o.metaData){
12531             
12532             delete this.ef;
12533             this.metaFromRemote = true;
12534             this.meta = o.metaData;
12535             this.recordType = Roo.data.Record.create(o.metaData.fields);
12536             this.onMetaChange(this.meta, this.recordType, o);
12537         }
12538         return this.readRecords(o);
12539     },
12540
12541     // private function a store will implement
12542     onMetaChange : function(meta, recordType, o){
12543
12544     },
12545
12546     /**
12547          * @ignore
12548          */
12549     simpleAccess: function(obj, subsc) {
12550         return obj[subsc];
12551     },
12552
12553         /**
12554          * @ignore
12555          */
12556     getJsonAccessor: function(){
12557         var re = /[\[\.]/;
12558         return function(expr) {
12559             try {
12560                 return(re.test(expr))
12561                     ? new Function("obj", "return obj." + expr)
12562                     : function(obj){
12563                         return obj[expr];
12564                     };
12565             } catch(e){}
12566             return Roo.emptyFn;
12567         };
12568     }(),
12569
12570     /**
12571      * Create a data block containing Roo.data.Records from an XML document.
12572      * @param {Object} o An object which contains an Array of row objects in the property specified
12573      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12574      * which contains the total size of the dataset.
12575      * @return {Object} data A data block which is used by an Roo.data.Store object as
12576      * a cache of Roo.data.Records.
12577      */
12578     readRecords : function(o){
12579         /**
12580          * After any data loads, the raw JSON data is available for further custom processing.
12581          * @type Object
12582          */
12583         this.o = o;
12584         var s = this.meta, Record = this.recordType,
12585             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12586
12587 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12588         if (!this.ef) {
12589             if(s.totalProperty) {
12590                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12591                 }
12592                 if(s.successProperty) {
12593                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12594                 }
12595                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12596                 if (s.id) {
12597                         var g = this.getJsonAccessor(s.id);
12598                         this.getId = function(rec) {
12599                                 var r = g(rec);  
12600                                 return (r === undefined || r === "") ? null : r;
12601                         };
12602                 } else {
12603                         this.getId = function(){return null;};
12604                 }
12605             this.ef = [];
12606             for(var jj = 0; jj < fl; jj++){
12607                 f = fi[jj];
12608                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12609                 this.ef[jj] = this.getJsonAccessor(map);
12610             }
12611         }
12612
12613         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12614         if(s.totalProperty){
12615             var vt = parseInt(this.getTotal(o), 10);
12616             if(!isNaN(vt)){
12617                 totalRecords = vt;
12618             }
12619         }
12620         if(s.successProperty){
12621             var vs = this.getSuccess(o);
12622             if(vs === false || vs === 'false'){
12623                 success = false;
12624             }
12625         }
12626         var records = [];
12627         for(var i = 0; i < c; i++){
12628                 var n = root[i];
12629             var values = {};
12630             var id = this.getId(n);
12631             for(var j = 0; j < fl; j++){
12632                 f = fi[j];
12633             var v = this.ef[j](n);
12634             if (!f.convert) {
12635                 Roo.log('missing convert for ' + f.name);
12636                 Roo.log(f);
12637                 continue;
12638             }
12639             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12640             }
12641             var record = new Record(values, id);
12642             record.json = n;
12643             records[i] = record;
12644         }
12645         return {
12646             raw : o,
12647             success : success,
12648             records : records,
12649             totalRecords : totalRecords
12650         };
12651     }
12652 });/*
12653  * Based on:
12654  * Ext JS Library 1.1.1
12655  * Copyright(c) 2006-2007, Ext JS, LLC.
12656  *
12657  * Originally Released Under LGPL - original licence link has changed is not relivant.
12658  *
12659  * Fork - LGPL
12660  * <script type="text/javascript">
12661  */
12662
12663 /**
12664  * @class Roo.data.ArrayReader
12665  * @extends Roo.data.DataReader
12666  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12667  * Each element of that Array represents a row of data fields. The
12668  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12669  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12670  * <p>
12671  * Example code:.
12672  * <pre><code>
12673 var RecordDef = Roo.data.Record.create([
12674     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12675     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12676 ]);
12677 var myReader = new Roo.data.ArrayReader({
12678     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12679 }, RecordDef);
12680 </code></pre>
12681  * <p>
12682  * This would consume an Array like this:
12683  * <pre><code>
12684 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12685   </code></pre>
12686  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12687  * @constructor
12688  * Create a new JsonReader
12689  * @param {Object} meta Metadata configuration options.
12690  * @param {Object} recordType Either an Array of field definition objects
12691  * as specified to {@link Roo.data.Record#create},
12692  * or an {@link Roo.data.Record} object
12693  * created using {@link Roo.data.Record#create}.
12694  */
12695 Roo.data.ArrayReader = function(meta, recordType){
12696     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12697 };
12698
12699 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12700     /**
12701      * Create a data block containing Roo.data.Records from an XML document.
12702      * @param {Object} o An Array of row objects which represents the dataset.
12703      * @return {Object} data A data block which is used by an Roo.data.Store object as
12704      * a cache of Roo.data.Records.
12705      */
12706     readRecords : function(o){
12707         var sid = this.meta ? this.meta.id : null;
12708         var recordType = this.recordType, fields = recordType.prototype.fields;
12709         var records = [];
12710         var root = o;
12711             for(var i = 0; i < root.length; i++){
12712                     var n = root[i];
12713                 var values = {};
12714                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12715                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12716                 var f = fields.items[j];
12717                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12718                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12719                 v = f.convert(v);
12720                 values[f.name] = v;
12721             }
12722                 var record = new recordType(values, id);
12723                 record.json = n;
12724                 records[records.length] = record;
12725             }
12726             return {
12727                 records : records,
12728                 totalRecords : records.length
12729             };
12730     }
12731 });/*
12732  * - LGPL
12733  * * 
12734  */
12735
12736 /**
12737  * @class Roo.bootstrap.ComboBox
12738  * @extends Roo.bootstrap.TriggerField
12739  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12740  * @cfg {Boolean} append (true|false) default false
12741  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12742  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12743  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12744  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12745  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12746  * @cfg {Boolean} animate default true
12747  * @cfg {Boolean} emptyResultText only for touch device
12748  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12749  * @cfg {String} emptyTitle default ''
12750  * @constructor
12751  * Create a new ComboBox.
12752  * @param {Object} config Configuration options
12753  */
12754 Roo.bootstrap.ComboBox = function(config){
12755     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12756     this.addEvents({
12757         /**
12758          * @event expand
12759          * Fires when the dropdown list is expanded
12760         * @param {Roo.bootstrap.ComboBox} combo This combo box
12761         */
12762         'expand' : true,
12763         /**
12764          * @event collapse
12765          * Fires when the dropdown list is collapsed
12766         * @param {Roo.bootstrap.ComboBox} combo This combo box
12767         */
12768         'collapse' : true,
12769         /**
12770          * @event beforeselect
12771          * Fires before a list item is selected. Return false to cancel the selection.
12772         * @param {Roo.bootstrap.ComboBox} combo This combo box
12773         * @param {Roo.data.Record} record The data record returned from the underlying store
12774         * @param {Number} index The index of the selected item in the dropdown list
12775         */
12776         'beforeselect' : true,
12777         /**
12778          * @event select
12779          * Fires when a list item is selected
12780         * @param {Roo.bootstrap.ComboBox} combo This combo box
12781         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12782         * @param {Number} index The index of the selected item in the dropdown list
12783         */
12784         'select' : true,
12785         /**
12786          * @event beforequery
12787          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12788          * The event object passed has these properties:
12789         * @param {Roo.bootstrap.ComboBox} combo This combo box
12790         * @param {String} query The query
12791         * @param {Boolean} forceAll true to force "all" query
12792         * @param {Boolean} cancel true to cancel the query
12793         * @param {Object} e The query event object
12794         */
12795         'beforequery': true,
12796          /**
12797          * @event add
12798          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12799         * @param {Roo.bootstrap.ComboBox} combo This combo box
12800         */
12801         'add' : true,
12802         /**
12803          * @event edit
12804          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12805         * @param {Roo.bootstrap.ComboBox} combo This combo box
12806         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12807         */
12808         'edit' : true,
12809         /**
12810          * @event remove
12811          * Fires when the remove value from the combobox array
12812         * @param {Roo.bootstrap.ComboBox} combo This combo box
12813         */
12814         'remove' : true,
12815         /**
12816          * @event afterremove
12817          * Fires when the remove value from the combobox array
12818         * @param {Roo.bootstrap.ComboBox} combo This combo box
12819         */
12820         'afterremove' : true,
12821         /**
12822          * @event specialfilter
12823          * Fires when specialfilter
12824             * @param {Roo.bootstrap.ComboBox} combo This combo box
12825             */
12826         'specialfilter' : true,
12827         /**
12828          * @event tick
12829          * Fires when tick the element
12830             * @param {Roo.bootstrap.ComboBox} combo This combo box
12831             */
12832         'tick' : true,
12833         /**
12834          * @event touchviewdisplay
12835          * Fires when touch view require special display (default is using displayField)
12836             * @param {Roo.bootstrap.ComboBox} combo This combo box
12837             * @param {Object} cfg set html .
12838             */
12839         'touchviewdisplay' : true
12840         
12841     });
12842     
12843     this.item = [];
12844     this.tickItems = [];
12845     
12846     this.selectedIndex = -1;
12847     if(this.mode == 'local'){
12848         if(config.queryDelay === undefined){
12849             this.queryDelay = 10;
12850         }
12851         if(config.minChars === undefined){
12852             this.minChars = 0;
12853         }
12854     }
12855 };
12856
12857 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12858      
12859     /**
12860      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12861      * rendering into an Roo.Editor, defaults to false)
12862      */
12863     /**
12864      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12865      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12866      */
12867     /**
12868      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12869      */
12870     /**
12871      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12872      * the dropdown list (defaults to undefined, with no header element)
12873      */
12874
12875      /**
12876      * @cfg {String/Roo.Template} tpl The template to use to render the output
12877      */
12878      
12879      /**
12880      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12881      */
12882     listWidth: undefined,
12883     /**
12884      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12885      * mode = 'remote' or 'text' if mode = 'local')
12886      */
12887     displayField: undefined,
12888     
12889     /**
12890      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12891      * mode = 'remote' or 'value' if mode = 'local'). 
12892      * Note: use of a valueField requires the user make a selection
12893      * in order for a value to be mapped.
12894      */
12895     valueField: undefined,
12896     /**
12897      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12898      */
12899     modalTitle : '',
12900     
12901     /**
12902      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12903      * field's data value (defaults to the underlying DOM element's name)
12904      */
12905     hiddenName: undefined,
12906     /**
12907      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12908      */
12909     listClass: '',
12910     /**
12911      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12912      */
12913     selectedClass: 'active',
12914     
12915     /**
12916      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12917      */
12918     shadow:'sides',
12919     /**
12920      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12921      * anchor positions (defaults to 'tl-bl')
12922      */
12923     listAlign: 'tl-bl?',
12924     /**
12925      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12926      */
12927     maxHeight: 300,
12928     /**
12929      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12930      * query specified by the allQuery config option (defaults to 'query')
12931      */
12932     triggerAction: 'query',
12933     /**
12934      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12935      * (defaults to 4, does not apply if editable = false)
12936      */
12937     minChars : 4,
12938     /**
12939      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12940      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12941      */
12942     typeAhead: false,
12943     /**
12944      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12945      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12946      */
12947     queryDelay: 500,
12948     /**
12949      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12950      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12951      */
12952     pageSize: 0,
12953     /**
12954      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12955      * when editable = true (defaults to false)
12956      */
12957     selectOnFocus:false,
12958     /**
12959      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12960      */
12961     queryParam: 'query',
12962     /**
12963      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12964      * when mode = 'remote' (defaults to 'Loading...')
12965      */
12966     loadingText: 'Loading...',
12967     /**
12968      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12969      */
12970     resizable: false,
12971     /**
12972      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12973      */
12974     handleHeight : 8,
12975     /**
12976      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12977      * traditional select (defaults to true)
12978      */
12979     editable: true,
12980     /**
12981      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12982      */
12983     allQuery: '',
12984     /**
12985      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12986      */
12987     mode: 'remote',
12988     /**
12989      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12990      * listWidth has a higher value)
12991      */
12992     minListWidth : 70,
12993     /**
12994      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12995      * allow the user to set arbitrary text into the field (defaults to false)
12996      */
12997     forceSelection:false,
12998     /**
12999      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13000      * if typeAhead = true (defaults to 250)
13001      */
13002     typeAheadDelay : 250,
13003     /**
13004      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13005      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13006      */
13007     valueNotFoundText : undefined,
13008     /**
13009      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13010      */
13011     blockFocus : false,
13012     
13013     /**
13014      * @cfg {Boolean} disableClear Disable showing of clear button.
13015      */
13016     disableClear : false,
13017     /**
13018      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13019      */
13020     alwaysQuery : false,
13021     
13022     /**
13023      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13024      */
13025     multiple : false,
13026     
13027     /**
13028      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13029      */
13030     invalidClass : "has-warning",
13031     
13032     /**
13033      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13034      */
13035     validClass : "has-success",
13036     
13037     /**
13038      * @cfg {Boolean} specialFilter (true|false) special filter default false
13039      */
13040     specialFilter : false,
13041     
13042     /**
13043      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13044      */
13045     mobileTouchView : true,
13046     
13047     /**
13048      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13049      */
13050     useNativeIOS : false,
13051     
13052     /**
13053      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13054      */
13055     mobile_restrict_height : false,
13056     
13057     ios_options : false,
13058     
13059     //private
13060     addicon : false,
13061     editicon: false,
13062     
13063     page: 0,
13064     hasQuery: false,
13065     append: false,
13066     loadNext: false,
13067     autoFocus : true,
13068     tickable : false,
13069     btnPosition : 'right',
13070     triggerList : true,
13071     showToggleBtn : true,
13072     animate : true,
13073     emptyResultText: 'Empty',
13074     triggerText : 'Select',
13075     emptyTitle : '',
13076     
13077     // element that contains real text value.. (when hidden is used..)
13078     
13079     getAutoCreate : function()
13080     {   
13081         var cfg = false;
13082         //render
13083         /*
13084          * Render classic select for iso
13085          */
13086         
13087         if(Roo.isIOS && this.useNativeIOS){
13088             cfg = this.getAutoCreateNativeIOS();
13089             return cfg;
13090         }
13091         
13092         /*
13093          * Touch Devices
13094          */
13095         
13096         if(Roo.isTouch && this.mobileTouchView){
13097             cfg = this.getAutoCreateTouchView();
13098             return cfg;;
13099         }
13100         
13101         /*
13102          *  Normal ComboBox
13103          */
13104         if(!this.tickable){
13105             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13106             return cfg;
13107         }
13108         
13109         /*
13110          *  ComboBox with tickable selections
13111          */
13112              
13113         var align = this.labelAlign || this.parentLabelAlign();
13114         
13115         cfg = {
13116             cls : 'form-group roo-combobox-tickable' //input-group
13117         };
13118         
13119         var btn_text_select = '';
13120         var btn_text_done = '';
13121         var btn_text_cancel = '';
13122         
13123         if (this.btn_text_show) {
13124             btn_text_select = 'Select';
13125             btn_text_done = 'Done';
13126             btn_text_cancel = 'Cancel'; 
13127         }
13128         
13129         var buttons = {
13130             tag : 'div',
13131             cls : 'tickable-buttons',
13132             cn : [
13133                 {
13134                     tag : 'button',
13135                     type : 'button',
13136                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13137                     //html : this.triggerText
13138                     html: btn_text_select
13139                 },
13140                 {
13141                     tag : 'button',
13142                     type : 'button',
13143                     name : 'ok',
13144                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13145                     //html : 'Done'
13146                     html: btn_text_done
13147                 },
13148                 {
13149                     tag : 'button',
13150                     type : 'button',
13151                     name : 'cancel',
13152                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13153                     //html : 'Cancel'
13154                     html: btn_text_cancel
13155                 }
13156             ]
13157         };
13158         
13159         if(this.editable){
13160             buttons.cn.unshift({
13161                 tag: 'input',
13162                 cls: 'roo-select2-search-field-input'
13163             });
13164         }
13165         
13166         var _this = this;
13167         
13168         Roo.each(buttons.cn, function(c){
13169             if (_this.size) {
13170                 c.cls += ' btn-' + _this.size;
13171             }
13172
13173             if (_this.disabled) {
13174                 c.disabled = true;
13175             }
13176         });
13177         
13178         var box = {
13179             tag: 'div',
13180             cn: [
13181                 {
13182                     tag: 'input',
13183                     type : 'hidden',
13184                     cls: 'form-hidden-field'
13185                 },
13186                 {
13187                     tag: 'ul',
13188                     cls: 'roo-select2-choices',
13189                     cn:[
13190                         {
13191                             tag: 'li',
13192                             cls: 'roo-select2-search-field',
13193                             cn: [
13194                                 buttons
13195                             ]
13196                         }
13197                     ]
13198                 }
13199             ]
13200         };
13201         
13202         var combobox = {
13203             cls: 'roo-select2-container input-group roo-select2-container-multi',
13204             cn: [
13205                 box
13206 //                {
13207 //                    tag: 'ul',
13208 //                    cls: 'typeahead typeahead-long dropdown-menu',
13209 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13210 //                }
13211             ]
13212         };
13213         
13214         if(this.hasFeedback && !this.allowBlank){
13215             
13216             var feedback = {
13217                 tag: 'span',
13218                 cls: 'glyphicon form-control-feedback'
13219             };
13220
13221             combobox.cn.push(feedback);
13222         }
13223         
13224         
13225         if (align ==='left' && this.fieldLabel.length) {
13226             
13227             cfg.cls += ' roo-form-group-label-left';
13228             
13229             cfg.cn = [
13230                 {
13231                     tag : 'i',
13232                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13233                     tooltip : 'This field is required'
13234                 },
13235                 {
13236                     tag: 'label',
13237                     'for' :  id,
13238                     cls : 'control-label',
13239                     html : this.fieldLabel
13240
13241                 },
13242                 {
13243                     cls : "", 
13244                     cn: [
13245                         combobox
13246                     ]
13247                 }
13248
13249             ];
13250             
13251             var labelCfg = cfg.cn[1];
13252             var contentCfg = cfg.cn[2];
13253             
13254
13255             if(this.indicatorpos == 'right'){
13256                 
13257                 cfg.cn = [
13258                     {
13259                         tag: 'label',
13260                         'for' :  id,
13261                         cls : 'control-label',
13262                         cn : [
13263                             {
13264                                 tag : 'span',
13265                                 html : this.fieldLabel
13266                             },
13267                             {
13268                                 tag : 'i',
13269                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13270                                 tooltip : 'This field is required'
13271                             }
13272                         ]
13273                     },
13274                     {
13275                         cls : "",
13276                         cn: [
13277                             combobox
13278                         ]
13279                     }
13280
13281                 ];
13282                 
13283                 
13284                 
13285                 labelCfg = cfg.cn[0];
13286                 contentCfg = cfg.cn[1];
13287             
13288             }
13289             
13290             if(this.labelWidth > 12){
13291                 labelCfg.style = "width: " + this.labelWidth + 'px';
13292             }
13293             
13294             if(this.labelWidth < 13 && this.labelmd == 0){
13295                 this.labelmd = this.labelWidth;
13296             }
13297             
13298             if(this.labellg > 0){
13299                 labelCfg.cls += ' col-lg-' + this.labellg;
13300                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13301             }
13302             
13303             if(this.labelmd > 0){
13304                 labelCfg.cls += ' col-md-' + this.labelmd;
13305                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13306             }
13307             
13308             if(this.labelsm > 0){
13309                 labelCfg.cls += ' col-sm-' + this.labelsm;
13310                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13311             }
13312             
13313             if(this.labelxs > 0){
13314                 labelCfg.cls += ' col-xs-' + this.labelxs;
13315                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13316             }
13317                 
13318                 
13319         } else if ( this.fieldLabel.length) {
13320 //                Roo.log(" label");
13321                  cfg.cn = [
13322                     {
13323                         tag : 'i',
13324                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13325                         tooltip : 'This field is required'
13326                     },
13327                     {
13328                         tag: 'label',
13329                         //cls : 'input-group-addon',
13330                         html : this.fieldLabel
13331                     },
13332                     combobox
13333                 ];
13334                 
13335                 if(this.indicatorpos == 'right'){
13336                     cfg.cn = [
13337                         {
13338                             tag: 'label',
13339                             //cls : 'input-group-addon',
13340                             html : this.fieldLabel
13341                         },
13342                         {
13343                             tag : 'i',
13344                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13345                             tooltip : 'This field is required'
13346                         },
13347                         combobox
13348                     ];
13349                     
13350                 }
13351
13352         } else {
13353             
13354 //                Roo.log(" no label && no align");
13355                 cfg = combobox
13356                      
13357                 
13358         }
13359          
13360         var settings=this;
13361         ['xs','sm','md','lg'].map(function(size){
13362             if (settings[size]) {
13363                 cfg.cls += ' col-' + size + '-' + settings[size];
13364             }
13365         });
13366         
13367         return cfg;
13368         
13369     },
13370     
13371     _initEventsCalled : false,
13372     
13373     // private
13374     initEvents: function()
13375     {   
13376         if (this._initEventsCalled) { // as we call render... prevent looping...
13377             return;
13378         }
13379         this._initEventsCalled = true;
13380         
13381         if (!this.store) {
13382             throw "can not find store for combo";
13383         }
13384         
13385         this.indicator = this.indicatorEl();
13386         
13387         this.store = Roo.factory(this.store, Roo.data);
13388         this.store.parent = this;
13389         
13390         // if we are building from html. then this element is so complex, that we can not really
13391         // use the rendered HTML.
13392         // so we have to trash and replace the previous code.
13393         if (Roo.XComponent.build_from_html) {
13394             // remove this element....
13395             var e = this.el.dom, k=0;
13396             while (e ) { e = e.previousSibling;  ++k;}
13397
13398             this.el.remove();
13399             
13400             this.el=false;
13401             this.rendered = false;
13402             
13403             this.render(this.parent().getChildContainer(true), k);
13404         }
13405         
13406         if(Roo.isIOS && this.useNativeIOS){
13407             this.initIOSView();
13408             return;
13409         }
13410         
13411         /*
13412          * Touch Devices
13413          */
13414         
13415         if(Roo.isTouch && this.mobileTouchView){
13416             this.initTouchView();
13417             return;
13418         }
13419         
13420         if(this.tickable){
13421             this.initTickableEvents();
13422             return;
13423         }
13424         
13425         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13426         
13427         if(this.hiddenName){
13428             
13429             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13430             
13431             this.hiddenField.dom.value =
13432                 this.hiddenValue !== undefined ? this.hiddenValue :
13433                 this.value !== undefined ? this.value : '';
13434
13435             // prevent input submission
13436             this.el.dom.removeAttribute('name');
13437             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13438              
13439              
13440         }
13441         //if(Roo.isGecko){
13442         //    this.el.dom.setAttribute('autocomplete', 'off');
13443         //}
13444         
13445         var cls = 'x-combo-list';
13446         
13447         //this.list = new Roo.Layer({
13448         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13449         //});
13450         
13451         var _this = this;
13452         
13453         (function(){
13454             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13455             _this.list.setWidth(lw);
13456         }).defer(100);
13457         
13458         this.list.on('mouseover', this.onViewOver, this);
13459         this.list.on('mousemove', this.onViewMove, this);
13460         this.list.on('scroll', this.onViewScroll, this);
13461         
13462         /*
13463         this.list.swallowEvent('mousewheel');
13464         this.assetHeight = 0;
13465
13466         if(this.title){
13467             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13468             this.assetHeight += this.header.getHeight();
13469         }
13470
13471         this.innerList = this.list.createChild({cls:cls+'-inner'});
13472         this.innerList.on('mouseover', this.onViewOver, this);
13473         this.innerList.on('mousemove', this.onViewMove, this);
13474         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13475         
13476         if(this.allowBlank && !this.pageSize && !this.disableClear){
13477             this.footer = this.list.createChild({cls:cls+'-ft'});
13478             this.pageTb = new Roo.Toolbar(this.footer);
13479            
13480         }
13481         if(this.pageSize){
13482             this.footer = this.list.createChild({cls:cls+'-ft'});
13483             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13484                     {pageSize: this.pageSize});
13485             
13486         }
13487         
13488         if (this.pageTb && this.allowBlank && !this.disableClear) {
13489             var _this = this;
13490             this.pageTb.add(new Roo.Toolbar.Fill(), {
13491                 cls: 'x-btn-icon x-btn-clear',
13492                 text: '&#160;',
13493                 handler: function()
13494                 {
13495                     _this.collapse();
13496                     _this.clearValue();
13497                     _this.onSelect(false, -1);
13498                 }
13499             });
13500         }
13501         if (this.footer) {
13502             this.assetHeight += this.footer.getHeight();
13503         }
13504         */
13505             
13506         if(!this.tpl){
13507             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13508         }
13509
13510         this.view = new Roo.View(this.list, this.tpl, {
13511             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13512         });
13513         //this.view.wrapEl.setDisplayed(false);
13514         this.view.on('click', this.onViewClick, this);
13515         
13516         
13517         this.store.on('beforeload', this.onBeforeLoad, this);
13518         this.store.on('load', this.onLoad, this);
13519         this.store.on('loadexception', this.onLoadException, this);
13520         /*
13521         if(this.resizable){
13522             this.resizer = new Roo.Resizable(this.list,  {
13523                pinned:true, handles:'se'
13524             });
13525             this.resizer.on('resize', function(r, w, h){
13526                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13527                 this.listWidth = w;
13528                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13529                 this.restrictHeight();
13530             }, this);
13531             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13532         }
13533         */
13534         if(!this.editable){
13535             this.editable = true;
13536             this.setEditable(false);
13537         }
13538         
13539         /*
13540         
13541         if (typeof(this.events.add.listeners) != 'undefined') {
13542             
13543             this.addicon = this.wrap.createChild(
13544                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13545        
13546             this.addicon.on('click', function(e) {
13547                 this.fireEvent('add', this);
13548             }, this);
13549         }
13550         if (typeof(this.events.edit.listeners) != 'undefined') {
13551             
13552             this.editicon = this.wrap.createChild(
13553                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13554             if (this.addicon) {
13555                 this.editicon.setStyle('margin-left', '40px');
13556             }
13557             this.editicon.on('click', function(e) {
13558                 
13559                 // we fire even  if inothing is selected..
13560                 this.fireEvent('edit', this, this.lastData );
13561                 
13562             }, this);
13563         }
13564         */
13565         
13566         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13567             "up" : function(e){
13568                 this.inKeyMode = true;
13569                 this.selectPrev();
13570             },
13571
13572             "down" : function(e){
13573                 if(!this.isExpanded()){
13574                     this.onTriggerClick();
13575                 }else{
13576                     this.inKeyMode = true;
13577                     this.selectNext();
13578                 }
13579             },
13580
13581             "enter" : function(e){
13582 //                this.onViewClick();
13583                 //return true;
13584                 this.collapse();
13585                 
13586                 if(this.fireEvent("specialkey", this, e)){
13587                     this.onViewClick(false);
13588                 }
13589                 
13590                 return true;
13591             },
13592
13593             "esc" : function(e){
13594                 this.collapse();
13595             },
13596
13597             "tab" : function(e){
13598                 this.collapse();
13599                 
13600                 if(this.fireEvent("specialkey", this, e)){
13601                     this.onViewClick(false);
13602                 }
13603                 
13604                 return true;
13605             },
13606
13607             scope : this,
13608
13609             doRelay : function(foo, bar, hname){
13610                 if(hname == 'down' || this.scope.isExpanded()){
13611                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13612                 }
13613                 return true;
13614             },
13615
13616             forceKeyDown: true
13617         });
13618         
13619         
13620         this.queryDelay = Math.max(this.queryDelay || 10,
13621                 this.mode == 'local' ? 10 : 250);
13622         
13623         
13624         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13625         
13626         if(this.typeAhead){
13627             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13628         }
13629         if(this.editable !== false){
13630             this.inputEl().on("keyup", this.onKeyUp, this);
13631         }
13632         if(this.forceSelection){
13633             this.inputEl().on('blur', this.doForce, this);
13634         }
13635         
13636         if(this.multiple){
13637             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13638             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13639         }
13640     },
13641     
13642     initTickableEvents: function()
13643     {   
13644         this.createList();
13645         
13646         if(this.hiddenName){
13647             
13648             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13649             
13650             this.hiddenField.dom.value =
13651                 this.hiddenValue !== undefined ? this.hiddenValue :
13652                 this.value !== undefined ? this.value : '';
13653
13654             // prevent input submission
13655             this.el.dom.removeAttribute('name');
13656             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13657              
13658              
13659         }
13660         
13661 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13662         
13663         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13664         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13665         if(this.triggerList){
13666             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13667         }
13668          
13669         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13670         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13671         
13672         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13673         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13674         
13675         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13676         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13677         
13678         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13679         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13680         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13681         
13682         this.okBtn.hide();
13683         this.cancelBtn.hide();
13684         
13685         var _this = this;
13686         
13687         (function(){
13688             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13689             _this.list.setWidth(lw);
13690         }).defer(100);
13691         
13692         this.list.on('mouseover', this.onViewOver, this);
13693         this.list.on('mousemove', this.onViewMove, this);
13694         
13695         this.list.on('scroll', this.onViewScroll, this);
13696         
13697         if(!this.tpl){
13698             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13699                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13700         }
13701
13702         this.view = new Roo.View(this.list, this.tpl, {
13703             singleSelect:true,
13704             tickable:true,
13705             parent:this,
13706             store: this.store,
13707             selectedClass: this.selectedClass
13708         });
13709         
13710         //this.view.wrapEl.setDisplayed(false);
13711         this.view.on('click', this.onViewClick, this);
13712         
13713         
13714         
13715         this.store.on('beforeload', this.onBeforeLoad, this);
13716         this.store.on('load', this.onLoad, this);
13717         this.store.on('loadexception', this.onLoadException, this);
13718         
13719         if(this.editable){
13720             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13721                 "up" : function(e){
13722                     this.inKeyMode = true;
13723                     this.selectPrev();
13724                 },
13725
13726                 "down" : function(e){
13727                     this.inKeyMode = true;
13728                     this.selectNext();
13729                 },
13730
13731                 "enter" : function(e){
13732                     if(this.fireEvent("specialkey", this, e)){
13733                         this.onViewClick(false);
13734                     }
13735                     
13736                     return true;
13737                 },
13738
13739                 "esc" : function(e){
13740                     this.onTickableFooterButtonClick(e, false, false);
13741                 },
13742
13743                 "tab" : function(e){
13744                     this.fireEvent("specialkey", this, e);
13745                     
13746                     this.onTickableFooterButtonClick(e, false, false);
13747                     
13748                     return true;
13749                 },
13750
13751                 scope : this,
13752
13753                 doRelay : function(e, fn, key){
13754                     if(this.scope.isExpanded()){
13755                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13756                     }
13757                     return true;
13758                 },
13759
13760                 forceKeyDown: true
13761             });
13762         }
13763         
13764         this.queryDelay = Math.max(this.queryDelay || 10,
13765                 this.mode == 'local' ? 10 : 250);
13766         
13767         
13768         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13769         
13770         if(this.typeAhead){
13771             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13772         }
13773         
13774         if(this.editable !== false){
13775             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13776         }
13777         
13778         this.indicator = this.indicatorEl();
13779         
13780         if(this.indicator){
13781             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13782             this.indicator.hide();
13783         }
13784         
13785     },
13786
13787     onDestroy : function(){
13788         if(this.view){
13789             this.view.setStore(null);
13790             this.view.el.removeAllListeners();
13791             this.view.el.remove();
13792             this.view.purgeListeners();
13793         }
13794         if(this.list){
13795             this.list.dom.innerHTML  = '';
13796         }
13797         
13798         if(this.store){
13799             this.store.un('beforeload', this.onBeforeLoad, this);
13800             this.store.un('load', this.onLoad, this);
13801             this.store.un('loadexception', this.onLoadException, this);
13802         }
13803         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13804     },
13805
13806     // private
13807     fireKey : function(e){
13808         if(e.isNavKeyPress() && !this.list.isVisible()){
13809             this.fireEvent("specialkey", this, e);
13810         }
13811     },
13812
13813     // private
13814     onResize: function(w, h){
13815 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13816 //        
13817 //        if(typeof w != 'number'){
13818 //            // we do not handle it!?!?
13819 //            return;
13820 //        }
13821 //        var tw = this.trigger.getWidth();
13822 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13823 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13824 //        var x = w - tw;
13825 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13826 //            
13827 //        //this.trigger.setStyle('left', x+'px');
13828 //        
13829 //        if(this.list && this.listWidth === undefined){
13830 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13831 //            this.list.setWidth(lw);
13832 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13833 //        }
13834         
13835     
13836         
13837     },
13838
13839     /**
13840      * Allow or prevent the user from directly editing the field text.  If false is passed,
13841      * the user will only be able to select from the items defined in the dropdown list.  This method
13842      * is the runtime equivalent of setting the 'editable' config option at config time.
13843      * @param {Boolean} value True to allow the user to directly edit the field text
13844      */
13845     setEditable : function(value){
13846         if(value == this.editable){
13847             return;
13848         }
13849         this.editable = value;
13850         if(!value){
13851             this.inputEl().dom.setAttribute('readOnly', true);
13852             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13853             this.inputEl().addClass('x-combo-noedit');
13854         }else{
13855             this.inputEl().dom.setAttribute('readOnly', false);
13856             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13857             this.inputEl().removeClass('x-combo-noedit');
13858         }
13859     },
13860
13861     // private
13862     
13863     onBeforeLoad : function(combo,opts){
13864         if(!this.hasFocus){
13865             return;
13866         }
13867          if (!opts.add) {
13868             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13869          }
13870         this.restrictHeight();
13871         this.selectedIndex = -1;
13872     },
13873
13874     // private
13875     onLoad : function(){
13876         
13877         this.hasQuery = false;
13878         
13879         if(!this.hasFocus){
13880             return;
13881         }
13882         
13883         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13884             this.loading.hide();
13885         }
13886         
13887         if(this.store.getCount() > 0){
13888             
13889             this.expand();
13890             this.restrictHeight();
13891             if(this.lastQuery == this.allQuery){
13892                 if(this.editable && !this.tickable){
13893                     this.inputEl().dom.select();
13894                 }
13895                 
13896                 if(
13897                     !this.selectByValue(this.value, true) &&
13898                     this.autoFocus && 
13899                     (
13900                         !this.store.lastOptions ||
13901                         typeof(this.store.lastOptions.add) == 'undefined' || 
13902                         this.store.lastOptions.add != true
13903                     )
13904                 ){
13905                     this.select(0, true);
13906                 }
13907             }else{
13908                 if(this.autoFocus){
13909                     this.selectNext();
13910                 }
13911                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13912                     this.taTask.delay(this.typeAheadDelay);
13913                 }
13914             }
13915         }else{
13916             this.onEmptyResults();
13917         }
13918         
13919         //this.el.focus();
13920     },
13921     // private
13922     onLoadException : function()
13923     {
13924         this.hasQuery = false;
13925         
13926         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13927             this.loading.hide();
13928         }
13929         
13930         if(this.tickable && this.editable){
13931             return;
13932         }
13933         
13934         this.collapse();
13935         // only causes errors at present
13936         //Roo.log(this.store.reader.jsonData);
13937         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13938             // fixme
13939             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13940         //}
13941         
13942         
13943     },
13944     // private
13945     onTypeAhead : function(){
13946         if(this.store.getCount() > 0){
13947             var r = this.store.getAt(0);
13948             var newValue = r.data[this.displayField];
13949             var len = newValue.length;
13950             var selStart = this.getRawValue().length;
13951             
13952             if(selStart != len){
13953                 this.setRawValue(newValue);
13954                 this.selectText(selStart, newValue.length);
13955             }
13956         }
13957     },
13958
13959     // private
13960     onSelect : function(record, index){
13961         
13962         if(this.fireEvent('beforeselect', this, record, index) !== false){
13963         
13964             this.setFromData(index > -1 ? record.data : false);
13965             
13966             this.collapse();
13967             this.fireEvent('select', this, record, index);
13968         }
13969     },
13970
13971     /**
13972      * Returns the currently selected field value or empty string if no value is set.
13973      * @return {String} value The selected value
13974      */
13975     getValue : function()
13976     {
13977         if(Roo.isIOS && this.useNativeIOS){
13978             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13979         }
13980         
13981         if(this.multiple){
13982             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13983         }
13984         
13985         if(this.valueField){
13986             return typeof this.value != 'undefined' ? this.value : '';
13987         }else{
13988             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13989         }
13990     },
13991     
13992     getRawValue : function()
13993     {
13994         if(Roo.isIOS && this.useNativeIOS){
13995             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13996         }
13997         
13998         var v = this.inputEl().getValue();
13999         
14000         return v;
14001     },
14002
14003     /**
14004      * Clears any text/value currently set in the field
14005      */
14006     clearValue : function(){
14007         
14008         if(this.hiddenField){
14009             this.hiddenField.dom.value = '';
14010         }
14011         this.value = '';
14012         this.setRawValue('');
14013         this.lastSelectionText = '';
14014         this.lastData = false;
14015         
14016         var close = this.closeTriggerEl();
14017         
14018         if(close){
14019             close.hide();
14020         }
14021         
14022         this.validate();
14023         
14024     },
14025
14026     /**
14027      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14028      * will be displayed in the field.  If the value does not match the data value of an existing item,
14029      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14030      * Otherwise the field will be blank (although the value will still be set).
14031      * @param {String} value The value to match
14032      */
14033     setValue : function(v)
14034     {
14035         if(Roo.isIOS && this.useNativeIOS){
14036             this.setIOSValue(v);
14037             return;
14038         }
14039         
14040         if(this.multiple){
14041             this.syncValue();
14042             return;
14043         }
14044         
14045         var text = v;
14046         if(this.valueField){
14047             var r = this.findRecord(this.valueField, v);
14048             if(r){
14049                 text = r.data[this.displayField];
14050             }else if(this.valueNotFoundText !== undefined){
14051                 text = this.valueNotFoundText;
14052             }
14053         }
14054         this.lastSelectionText = text;
14055         if(this.hiddenField){
14056             this.hiddenField.dom.value = v;
14057         }
14058         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14059         this.value = v;
14060         
14061         var close = this.closeTriggerEl();
14062         
14063         if(close){
14064             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14065         }
14066         
14067         this.validate();
14068     },
14069     /**
14070      * @property {Object} the last set data for the element
14071      */
14072     
14073     lastData : false,
14074     /**
14075      * Sets the value of the field based on a object which is related to the record format for the store.
14076      * @param {Object} value the value to set as. or false on reset?
14077      */
14078     setFromData : function(o){
14079         
14080         if(this.multiple){
14081             this.addItem(o);
14082             return;
14083         }
14084             
14085         var dv = ''; // display value
14086         var vv = ''; // value value..
14087         this.lastData = o;
14088         if (this.displayField) {
14089             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14090         } else {
14091             // this is an error condition!!!
14092             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14093         }
14094         
14095         if(this.valueField){
14096             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14097         }
14098         
14099         var close = this.closeTriggerEl();
14100         
14101         if(close){
14102             if(dv.length || vv * 1 > 0){
14103                 close.show() ;
14104                 this.blockFocus=true;
14105             } else {
14106                 close.hide();
14107             }             
14108         }
14109         
14110         if(this.hiddenField){
14111             this.hiddenField.dom.value = vv;
14112             
14113             this.lastSelectionText = dv;
14114             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14115             this.value = vv;
14116             return;
14117         }
14118         // no hidden field.. - we store the value in 'value', but still display
14119         // display field!!!!
14120         this.lastSelectionText = dv;
14121         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14122         this.value = vv;
14123         
14124         
14125         
14126     },
14127     // private
14128     reset : function(){
14129         // overridden so that last data is reset..
14130         
14131         if(this.multiple){
14132             this.clearItem();
14133             return;
14134         }
14135         
14136         this.setValue(this.originalValue);
14137         //this.clearInvalid();
14138         this.lastData = false;
14139         if (this.view) {
14140             this.view.clearSelections();
14141         }
14142         
14143         this.validate();
14144     },
14145     // private
14146     findRecord : function(prop, value){
14147         var record;
14148         if(this.store.getCount() > 0){
14149             this.store.each(function(r){
14150                 if(r.data[prop] == value){
14151                     record = r;
14152                     return false;
14153                 }
14154                 return true;
14155             });
14156         }
14157         return record;
14158     },
14159     
14160     getName: function()
14161     {
14162         // returns hidden if it's set..
14163         if (!this.rendered) {return ''};
14164         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14165         
14166     },
14167     // private
14168     onViewMove : function(e, t){
14169         this.inKeyMode = false;
14170     },
14171
14172     // private
14173     onViewOver : function(e, t){
14174         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14175             return;
14176         }
14177         var item = this.view.findItemFromChild(t);
14178         
14179         if(item){
14180             var index = this.view.indexOf(item);
14181             this.select(index, false);
14182         }
14183     },
14184
14185     // private
14186     onViewClick : function(view, doFocus, el, e)
14187     {
14188         var index = this.view.getSelectedIndexes()[0];
14189         
14190         var r = this.store.getAt(index);
14191         
14192         if(this.tickable){
14193             
14194             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14195                 return;
14196             }
14197             
14198             var rm = false;
14199             var _this = this;
14200             
14201             Roo.each(this.tickItems, function(v,k){
14202                 
14203                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14204                     Roo.log(v);
14205                     _this.tickItems.splice(k, 1);
14206                     
14207                     if(typeof(e) == 'undefined' && view == false){
14208                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14209                     }
14210                     
14211                     rm = true;
14212                     return;
14213                 }
14214             });
14215             
14216             if(rm){
14217                 return;
14218             }
14219             
14220             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14221                 this.tickItems.push(r.data);
14222             }
14223             
14224             if(typeof(e) == 'undefined' && view == false){
14225                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14226             }
14227                     
14228             return;
14229         }
14230         
14231         if(r){
14232             this.onSelect(r, index);
14233         }
14234         if(doFocus !== false && !this.blockFocus){
14235             this.inputEl().focus();
14236         }
14237     },
14238
14239     // private
14240     restrictHeight : function(){
14241         //this.innerList.dom.style.height = '';
14242         //var inner = this.innerList.dom;
14243         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14244         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14245         //this.list.beginUpdate();
14246         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14247         this.list.alignTo(this.inputEl(), this.listAlign);
14248         this.list.alignTo(this.inputEl(), this.listAlign);
14249         //this.list.endUpdate();
14250     },
14251
14252     // private
14253     onEmptyResults : function(){
14254         
14255         if(this.tickable && this.editable){
14256             this.hasFocus = false;
14257             this.restrictHeight();
14258             return;
14259         }
14260         
14261         this.collapse();
14262     },
14263
14264     /**
14265      * Returns true if the dropdown list is expanded, else false.
14266      */
14267     isExpanded : function(){
14268         return this.list.isVisible();
14269     },
14270
14271     /**
14272      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14273      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14274      * @param {String} value The data value of the item to select
14275      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14276      * selected item if it is not currently in view (defaults to true)
14277      * @return {Boolean} True if the value matched an item in the list, else false
14278      */
14279     selectByValue : function(v, scrollIntoView){
14280         if(v !== undefined && v !== null){
14281             var r = this.findRecord(this.valueField || this.displayField, v);
14282             if(r){
14283                 this.select(this.store.indexOf(r), scrollIntoView);
14284                 return true;
14285             }
14286         }
14287         return false;
14288     },
14289
14290     /**
14291      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14292      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14293      * @param {Number} index The zero-based index of the list item to select
14294      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14295      * selected item if it is not currently in view (defaults to true)
14296      */
14297     select : function(index, scrollIntoView){
14298         this.selectedIndex = index;
14299         this.view.select(index);
14300         if(scrollIntoView !== false){
14301             var el = this.view.getNode(index);
14302             /*
14303              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14304              */
14305             if(el){
14306                 this.list.scrollChildIntoView(el, false);
14307             }
14308         }
14309     },
14310
14311     // private
14312     selectNext : function(){
14313         var ct = this.store.getCount();
14314         if(ct > 0){
14315             if(this.selectedIndex == -1){
14316                 this.select(0);
14317             }else if(this.selectedIndex < ct-1){
14318                 this.select(this.selectedIndex+1);
14319             }
14320         }
14321     },
14322
14323     // private
14324     selectPrev : function(){
14325         var ct = this.store.getCount();
14326         if(ct > 0){
14327             if(this.selectedIndex == -1){
14328                 this.select(0);
14329             }else if(this.selectedIndex != 0){
14330                 this.select(this.selectedIndex-1);
14331             }
14332         }
14333     },
14334
14335     // private
14336     onKeyUp : function(e){
14337         if(this.editable !== false && !e.isSpecialKey()){
14338             this.lastKey = e.getKey();
14339             this.dqTask.delay(this.queryDelay);
14340         }
14341     },
14342
14343     // private
14344     validateBlur : function(){
14345         return !this.list || !this.list.isVisible();   
14346     },
14347
14348     // private
14349     initQuery : function(){
14350         
14351         var v = this.getRawValue();
14352         
14353         if(this.tickable && this.editable){
14354             v = this.tickableInputEl().getValue();
14355         }
14356         
14357         this.doQuery(v);
14358     },
14359
14360     // private
14361     doForce : function(){
14362         if(this.inputEl().dom.value.length > 0){
14363             this.inputEl().dom.value =
14364                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14365              
14366         }
14367     },
14368
14369     /**
14370      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14371      * query allowing the query action to be canceled if needed.
14372      * @param {String} query The SQL query to execute
14373      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14374      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14375      * saved in the current store (defaults to false)
14376      */
14377     doQuery : function(q, forceAll){
14378         
14379         if(q === undefined || q === null){
14380             q = '';
14381         }
14382         var qe = {
14383             query: q,
14384             forceAll: forceAll,
14385             combo: this,
14386             cancel:false
14387         };
14388         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14389             return false;
14390         }
14391         q = qe.query;
14392         
14393         forceAll = qe.forceAll;
14394         if(forceAll === true || (q.length >= this.minChars)){
14395             
14396             this.hasQuery = true;
14397             
14398             if(this.lastQuery != q || this.alwaysQuery){
14399                 this.lastQuery = q;
14400                 if(this.mode == 'local'){
14401                     this.selectedIndex = -1;
14402                     if(forceAll){
14403                         this.store.clearFilter();
14404                     }else{
14405                         
14406                         if(this.specialFilter){
14407                             this.fireEvent('specialfilter', this);
14408                             this.onLoad();
14409                             return;
14410                         }
14411                         
14412                         this.store.filter(this.displayField, q);
14413                     }
14414                     
14415                     this.store.fireEvent("datachanged", this.store);
14416                     
14417                     this.onLoad();
14418                     
14419                     
14420                 }else{
14421                     
14422                     this.store.baseParams[this.queryParam] = q;
14423                     
14424                     var options = {params : this.getParams(q)};
14425                     
14426                     if(this.loadNext){
14427                         options.add = true;
14428                         options.params.start = this.page * this.pageSize;
14429                     }
14430                     
14431                     this.store.load(options);
14432                     
14433                     /*
14434                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14435                      *  we should expand the list on onLoad
14436                      *  so command out it
14437                      */
14438 //                    this.expand();
14439                 }
14440             }else{
14441                 this.selectedIndex = -1;
14442                 this.onLoad();   
14443             }
14444         }
14445         
14446         this.loadNext = false;
14447     },
14448     
14449     // private
14450     getParams : function(q){
14451         var p = {};
14452         //p[this.queryParam] = q;
14453         
14454         if(this.pageSize){
14455             p.start = 0;
14456             p.limit = this.pageSize;
14457         }
14458         return p;
14459     },
14460
14461     /**
14462      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14463      */
14464     collapse : function(){
14465         if(!this.isExpanded()){
14466             return;
14467         }
14468         
14469         this.list.hide();
14470         
14471         this.hasFocus = false;
14472         
14473         if(this.tickable){
14474             this.okBtn.hide();
14475             this.cancelBtn.hide();
14476             this.trigger.show();
14477             
14478             if(this.editable){
14479                 this.tickableInputEl().dom.value = '';
14480                 this.tickableInputEl().blur();
14481             }
14482             
14483         }
14484         
14485         Roo.get(document).un('mousedown', this.collapseIf, this);
14486         Roo.get(document).un('mousewheel', this.collapseIf, this);
14487         if (!this.editable) {
14488             Roo.get(document).un('keydown', this.listKeyPress, this);
14489         }
14490         this.fireEvent('collapse', this);
14491         
14492         this.validate();
14493     },
14494
14495     // private
14496     collapseIf : function(e){
14497         var in_combo  = e.within(this.el);
14498         var in_list =  e.within(this.list);
14499         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14500         
14501         if (in_combo || in_list || is_list) {
14502             //e.stopPropagation();
14503             return;
14504         }
14505         
14506         if(this.tickable){
14507             this.onTickableFooterButtonClick(e, false, false);
14508         }
14509
14510         this.collapse();
14511         
14512     },
14513
14514     /**
14515      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14516      */
14517     expand : function(){
14518        
14519         if(this.isExpanded() || !this.hasFocus){
14520             return;
14521         }
14522         
14523         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14524         this.list.setWidth(lw);
14525         
14526         Roo.log('expand');
14527         
14528         this.list.show();
14529         
14530         this.restrictHeight();
14531         
14532         if(this.tickable){
14533             
14534             this.tickItems = Roo.apply([], this.item);
14535             
14536             this.okBtn.show();
14537             this.cancelBtn.show();
14538             this.trigger.hide();
14539             
14540             if(this.editable){
14541                 this.tickableInputEl().focus();
14542             }
14543             
14544         }
14545         
14546         Roo.get(document).on('mousedown', this.collapseIf, this);
14547         Roo.get(document).on('mousewheel', this.collapseIf, this);
14548         if (!this.editable) {
14549             Roo.get(document).on('keydown', this.listKeyPress, this);
14550         }
14551         
14552         this.fireEvent('expand', this);
14553     },
14554
14555     // private
14556     // Implements the default empty TriggerField.onTriggerClick function
14557     onTriggerClick : function(e)
14558     {
14559         Roo.log('trigger click');
14560         
14561         if(this.disabled || !this.triggerList){
14562             return;
14563         }
14564         
14565         this.page = 0;
14566         this.loadNext = false;
14567         
14568         if(this.isExpanded()){
14569             this.collapse();
14570             if (!this.blockFocus) {
14571                 this.inputEl().focus();
14572             }
14573             
14574         }else {
14575             this.hasFocus = true;
14576             if(this.triggerAction == 'all') {
14577                 this.doQuery(this.allQuery, true);
14578             } else {
14579                 this.doQuery(this.getRawValue());
14580             }
14581             if (!this.blockFocus) {
14582                 this.inputEl().focus();
14583             }
14584         }
14585     },
14586     
14587     onTickableTriggerClick : function(e)
14588     {
14589         if(this.disabled){
14590             return;
14591         }
14592         
14593         this.page = 0;
14594         this.loadNext = false;
14595         this.hasFocus = true;
14596         
14597         if(this.triggerAction == 'all') {
14598             this.doQuery(this.allQuery, true);
14599         } else {
14600             this.doQuery(this.getRawValue());
14601         }
14602     },
14603     
14604     onSearchFieldClick : function(e)
14605     {
14606         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14607             this.onTickableFooterButtonClick(e, false, false);
14608             return;
14609         }
14610         
14611         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14612             return;
14613         }
14614         
14615         this.page = 0;
14616         this.loadNext = false;
14617         this.hasFocus = true;
14618         
14619         if(this.triggerAction == 'all') {
14620             this.doQuery(this.allQuery, true);
14621         } else {
14622             this.doQuery(this.getRawValue());
14623         }
14624     },
14625     
14626     listKeyPress : function(e)
14627     {
14628         //Roo.log('listkeypress');
14629         // scroll to first matching element based on key pres..
14630         if (e.isSpecialKey()) {
14631             return false;
14632         }
14633         var k = String.fromCharCode(e.getKey()).toUpperCase();
14634         //Roo.log(k);
14635         var match  = false;
14636         var csel = this.view.getSelectedNodes();
14637         var cselitem = false;
14638         if (csel.length) {
14639             var ix = this.view.indexOf(csel[0]);
14640             cselitem  = this.store.getAt(ix);
14641             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14642                 cselitem = false;
14643             }
14644             
14645         }
14646         
14647         this.store.each(function(v) { 
14648             if (cselitem) {
14649                 // start at existing selection.
14650                 if (cselitem.id == v.id) {
14651                     cselitem = false;
14652                 }
14653                 return true;
14654             }
14655                 
14656             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14657                 match = this.store.indexOf(v);
14658                 return false;
14659             }
14660             return true;
14661         }, this);
14662         
14663         if (match === false) {
14664             return true; // no more action?
14665         }
14666         // scroll to?
14667         this.view.select(match);
14668         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14669         sn.scrollIntoView(sn.dom.parentNode, false);
14670     },
14671     
14672     onViewScroll : function(e, t){
14673         
14674         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){
14675             return;
14676         }
14677         
14678         this.hasQuery = true;
14679         
14680         this.loading = this.list.select('.loading', true).first();
14681         
14682         if(this.loading === null){
14683             this.list.createChild({
14684                 tag: 'div',
14685                 cls: 'loading roo-select2-more-results roo-select2-active',
14686                 html: 'Loading more results...'
14687             });
14688             
14689             this.loading = this.list.select('.loading', true).first();
14690             
14691             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14692             
14693             this.loading.hide();
14694         }
14695         
14696         this.loading.show();
14697         
14698         var _combo = this;
14699         
14700         this.page++;
14701         this.loadNext = true;
14702         
14703         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14704         
14705         return;
14706     },
14707     
14708     addItem : function(o)
14709     {   
14710         var dv = ''; // display value
14711         
14712         if (this.displayField) {
14713             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14714         } else {
14715             // this is an error condition!!!
14716             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14717         }
14718         
14719         if(!dv.length){
14720             return;
14721         }
14722         
14723         var choice = this.choices.createChild({
14724             tag: 'li',
14725             cls: 'roo-select2-search-choice',
14726             cn: [
14727                 {
14728                     tag: 'div',
14729                     html: dv
14730                 },
14731                 {
14732                     tag: 'a',
14733                     href: '#',
14734                     cls: 'roo-select2-search-choice-close fa fa-times',
14735                     tabindex: '-1'
14736                 }
14737             ]
14738             
14739         }, this.searchField);
14740         
14741         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14742         
14743         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14744         
14745         this.item.push(o);
14746         
14747         this.lastData = o;
14748         
14749         this.syncValue();
14750         
14751         this.inputEl().dom.value = '';
14752         
14753         this.validate();
14754     },
14755     
14756     onRemoveItem : function(e, _self, o)
14757     {
14758         e.preventDefault();
14759         
14760         this.lastItem = Roo.apply([], this.item);
14761         
14762         var index = this.item.indexOf(o.data) * 1;
14763         
14764         if( index < 0){
14765             Roo.log('not this item?!');
14766             return;
14767         }
14768         
14769         this.item.splice(index, 1);
14770         o.item.remove();
14771         
14772         this.syncValue();
14773         
14774         this.fireEvent('remove', this, e);
14775         
14776         this.validate();
14777         
14778     },
14779     
14780     syncValue : function()
14781     {
14782         if(!this.item.length){
14783             this.clearValue();
14784             return;
14785         }
14786             
14787         var value = [];
14788         var _this = this;
14789         Roo.each(this.item, function(i){
14790             if(_this.valueField){
14791                 value.push(i[_this.valueField]);
14792                 return;
14793             }
14794
14795             value.push(i);
14796         });
14797
14798         this.value = value.join(',');
14799
14800         if(this.hiddenField){
14801             this.hiddenField.dom.value = this.value;
14802         }
14803         
14804         this.store.fireEvent("datachanged", this.store);
14805         
14806         this.validate();
14807     },
14808     
14809     clearItem : function()
14810     {
14811         if(!this.multiple){
14812             return;
14813         }
14814         
14815         this.item = [];
14816         
14817         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14818            c.remove();
14819         });
14820         
14821         this.syncValue();
14822         
14823         this.validate();
14824         
14825         if(this.tickable && !Roo.isTouch){
14826             this.view.refresh();
14827         }
14828     },
14829     
14830     inputEl: function ()
14831     {
14832         if(Roo.isIOS && this.useNativeIOS){
14833             return this.el.select('select.roo-ios-select', true).first();
14834         }
14835         
14836         if(Roo.isTouch && this.mobileTouchView){
14837             return this.el.select('input.form-control',true).first();
14838         }
14839         
14840         if(this.tickable){
14841             return this.searchField;
14842         }
14843         
14844         return this.el.select('input.form-control',true).first();
14845     },
14846     
14847     onTickableFooterButtonClick : function(e, btn, el)
14848     {
14849         e.preventDefault();
14850         
14851         this.lastItem = Roo.apply([], this.item);
14852         
14853         if(btn && btn.name == 'cancel'){
14854             this.tickItems = Roo.apply([], this.item);
14855             this.collapse();
14856             return;
14857         }
14858         
14859         this.clearItem();
14860         
14861         var _this = this;
14862         
14863         Roo.each(this.tickItems, function(o){
14864             _this.addItem(o);
14865         });
14866         
14867         this.collapse();
14868         
14869     },
14870     
14871     validate : function()
14872     {
14873         if(this.getVisibilityEl().hasClass('hidden')){
14874             return true;
14875         }
14876         
14877         var v = this.getRawValue();
14878         
14879         if(this.multiple){
14880             v = this.getValue();
14881         }
14882         
14883         if(this.disabled || this.allowBlank || v.length){
14884             this.markValid();
14885             return true;
14886         }
14887         
14888         this.markInvalid();
14889         return false;
14890     },
14891     
14892     tickableInputEl : function()
14893     {
14894         if(!this.tickable || !this.editable){
14895             return this.inputEl();
14896         }
14897         
14898         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14899     },
14900     
14901     
14902     getAutoCreateTouchView : function()
14903     {
14904         var id = Roo.id();
14905         
14906         var cfg = {
14907             cls: 'form-group' //input-group
14908         };
14909         
14910         var input =  {
14911             tag: 'input',
14912             id : id,
14913             type : this.inputType,
14914             cls : 'form-control x-combo-noedit',
14915             autocomplete: 'new-password',
14916             placeholder : this.placeholder || '',
14917             readonly : true
14918         };
14919         
14920         if (this.name) {
14921             input.name = this.name;
14922         }
14923         
14924         if (this.size) {
14925             input.cls += ' input-' + this.size;
14926         }
14927         
14928         if (this.disabled) {
14929             input.disabled = true;
14930         }
14931         
14932         var inputblock = {
14933             cls : '',
14934             cn : [
14935                 input
14936             ]
14937         };
14938         
14939         if(this.before){
14940             inputblock.cls += ' input-group';
14941             
14942             inputblock.cn.unshift({
14943                 tag :'span',
14944                 cls : 'input-group-addon',
14945                 html : this.before
14946             });
14947         }
14948         
14949         if(this.removable && !this.multiple){
14950             inputblock.cls += ' roo-removable';
14951             
14952             inputblock.cn.push({
14953                 tag: 'button',
14954                 html : 'x',
14955                 cls : 'roo-combo-removable-btn close'
14956             });
14957         }
14958
14959         if(this.hasFeedback && !this.allowBlank){
14960             
14961             inputblock.cls += ' has-feedback';
14962             
14963             inputblock.cn.push({
14964                 tag: 'span',
14965                 cls: 'glyphicon form-control-feedback'
14966             });
14967             
14968         }
14969         
14970         if (this.after) {
14971             
14972             inputblock.cls += (this.before) ? '' : ' input-group';
14973             
14974             inputblock.cn.push({
14975                 tag :'span',
14976                 cls : 'input-group-addon',
14977                 html : this.after
14978             });
14979         }
14980
14981         var box = {
14982             tag: 'div',
14983             cn: [
14984                 {
14985                     tag: 'input',
14986                     type : 'hidden',
14987                     cls: 'form-hidden-field'
14988                 },
14989                 inputblock
14990             ]
14991             
14992         };
14993         
14994         if(this.multiple){
14995             box = {
14996                 tag: 'div',
14997                 cn: [
14998                     {
14999                         tag: 'input',
15000                         type : 'hidden',
15001                         cls: 'form-hidden-field'
15002                     },
15003                     {
15004                         tag: 'ul',
15005                         cls: 'roo-select2-choices',
15006                         cn:[
15007                             {
15008                                 tag: 'li',
15009                                 cls: 'roo-select2-search-field',
15010                                 cn: [
15011
15012                                     inputblock
15013                                 ]
15014                             }
15015                         ]
15016                     }
15017                 ]
15018             }
15019         };
15020         
15021         var combobox = {
15022             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15023             cn: [
15024                 box
15025             ]
15026         };
15027         
15028         if(!this.multiple && this.showToggleBtn){
15029             
15030             var caret = {
15031                         tag: 'span',
15032                         cls: 'caret'
15033             };
15034             
15035             if (this.caret != false) {
15036                 caret = {
15037                      tag: 'i',
15038                      cls: 'fa fa-' + this.caret
15039                 };
15040                 
15041             }
15042             
15043             combobox.cn.push({
15044                 tag :'span',
15045                 cls : 'input-group-addon btn dropdown-toggle',
15046                 cn : [
15047                     caret,
15048                     {
15049                         tag: 'span',
15050                         cls: 'combobox-clear',
15051                         cn  : [
15052                             {
15053                                 tag : 'i',
15054                                 cls: 'icon-remove'
15055                             }
15056                         ]
15057                     }
15058                 ]
15059
15060             })
15061         }
15062         
15063         if(this.multiple){
15064             combobox.cls += ' roo-select2-container-multi';
15065         }
15066         
15067         var align = this.labelAlign || this.parentLabelAlign();
15068         
15069         if (align ==='left' && this.fieldLabel.length) {
15070
15071             cfg.cn = [
15072                 {
15073                    tag : 'i',
15074                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15075                    tooltip : 'This field is required'
15076                 },
15077                 {
15078                     tag: 'label',
15079                     cls : 'control-label',
15080                     html : this.fieldLabel
15081
15082                 },
15083                 {
15084                     cls : '', 
15085                     cn: [
15086                         combobox
15087                     ]
15088                 }
15089             ];
15090             
15091             var labelCfg = cfg.cn[1];
15092             var contentCfg = cfg.cn[2];
15093             
15094
15095             if(this.indicatorpos == 'right'){
15096                 cfg.cn = [
15097                     {
15098                         tag: 'label',
15099                         'for' :  id,
15100                         cls : 'control-label',
15101                         cn : [
15102                             {
15103                                 tag : 'span',
15104                                 html : this.fieldLabel
15105                             },
15106                             {
15107                                 tag : 'i',
15108                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15109                                 tooltip : 'This field is required'
15110                             }
15111                         ]
15112                     },
15113                     {
15114                         cls : "",
15115                         cn: [
15116                             combobox
15117                         ]
15118                     }
15119
15120                 ];
15121                 
15122                 labelCfg = cfg.cn[0];
15123                 contentCfg = cfg.cn[1];
15124             }
15125             
15126            
15127             
15128             if(this.labelWidth > 12){
15129                 labelCfg.style = "width: " + this.labelWidth + 'px';
15130             }
15131             
15132             if(this.labelWidth < 13 && this.labelmd == 0){
15133                 this.labelmd = this.labelWidth;
15134             }
15135             
15136             if(this.labellg > 0){
15137                 labelCfg.cls += ' col-lg-' + this.labellg;
15138                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15139             }
15140             
15141             if(this.labelmd > 0){
15142                 labelCfg.cls += ' col-md-' + this.labelmd;
15143                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15144             }
15145             
15146             if(this.labelsm > 0){
15147                 labelCfg.cls += ' col-sm-' + this.labelsm;
15148                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15149             }
15150             
15151             if(this.labelxs > 0){
15152                 labelCfg.cls += ' col-xs-' + this.labelxs;
15153                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15154             }
15155                 
15156                 
15157         } else if ( this.fieldLabel.length) {
15158             cfg.cn = [
15159                 {
15160                    tag : 'i',
15161                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15162                    tooltip : 'This field is required'
15163                 },
15164                 {
15165                     tag: 'label',
15166                     cls : 'control-label',
15167                     html : this.fieldLabel
15168
15169                 },
15170                 {
15171                     cls : '', 
15172                     cn: [
15173                         combobox
15174                     ]
15175                 }
15176             ];
15177             
15178             if(this.indicatorpos == 'right'){
15179                 cfg.cn = [
15180                     {
15181                         tag: 'label',
15182                         cls : 'control-label',
15183                         html : this.fieldLabel,
15184                         cn : [
15185                             {
15186                                tag : 'i',
15187                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15188                                tooltip : 'This field is required'
15189                             }
15190                         ]
15191                     },
15192                     {
15193                         cls : '', 
15194                         cn: [
15195                             combobox
15196                         ]
15197                     }
15198                 ];
15199             }
15200         } else {
15201             cfg.cn = combobox;    
15202         }
15203         
15204         
15205         var settings = this;
15206         
15207         ['xs','sm','md','lg'].map(function(size){
15208             if (settings[size]) {
15209                 cfg.cls += ' col-' + size + '-' + settings[size];
15210             }
15211         });
15212         
15213         return cfg;
15214     },
15215     
15216     initTouchView : function()
15217     {
15218         this.renderTouchView();
15219         
15220         this.touchViewEl.on('scroll', function(){
15221             this.el.dom.scrollTop = 0;
15222         }, this);
15223         
15224         this.originalValue = this.getValue();
15225         
15226         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15227         
15228         this.inputEl().on("click", this.showTouchView, this);
15229         if (this.triggerEl) {
15230             this.triggerEl.on("click", this.showTouchView, this);
15231         }
15232         
15233         
15234         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15235         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15236         
15237         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15238         
15239         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15240         this.store.on('load', this.onTouchViewLoad, this);
15241         this.store.on('loadexception', this.onTouchViewLoadException, this);
15242         
15243         if(this.hiddenName){
15244             
15245             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15246             
15247             this.hiddenField.dom.value =
15248                 this.hiddenValue !== undefined ? this.hiddenValue :
15249                 this.value !== undefined ? this.value : '';
15250         
15251             this.el.dom.removeAttribute('name');
15252             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15253         }
15254         
15255         if(this.multiple){
15256             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15257             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15258         }
15259         
15260         if(this.removable && !this.multiple){
15261             var close = this.closeTriggerEl();
15262             if(close){
15263                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15264                 close.on('click', this.removeBtnClick, this, close);
15265             }
15266         }
15267         /*
15268          * fix the bug in Safari iOS8
15269          */
15270         this.inputEl().on("focus", function(e){
15271             document.activeElement.blur();
15272         }, this);
15273         
15274         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15275         
15276         return;
15277         
15278         
15279     },
15280     
15281     renderTouchView : function()
15282     {
15283         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15284         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15285         
15286         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15287         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15288         
15289         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15290         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15291         this.touchViewBodyEl.setStyle('overflow', 'auto');
15292         
15293         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15294         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15295         
15296         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15297         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15298         
15299     },
15300     
15301     showTouchView : function()
15302     {
15303         if(this.disabled){
15304             return;
15305         }
15306         
15307         this.touchViewHeaderEl.hide();
15308
15309         if(this.modalTitle.length){
15310             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15311             this.touchViewHeaderEl.show();
15312         }
15313
15314         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15315         this.touchViewEl.show();
15316
15317         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15318         
15319         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15320         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15321
15322         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15323
15324         if(this.modalTitle.length){
15325             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15326         }
15327         
15328         this.touchViewBodyEl.setHeight(bodyHeight);
15329
15330         if(this.animate){
15331             var _this = this;
15332             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15333         }else{
15334             this.touchViewEl.addClass('in');
15335         }
15336         
15337         if(this._touchViewMask){
15338             Roo.get(document.body).addClass("x-body-masked");
15339             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15340             this._touchViewMask.setStyle('z-index', 10000);
15341             this._touchViewMask.addClass('show');
15342         }
15343         
15344         this.doTouchViewQuery();
15345         
15346     },
15347     
15348     hideTouchView : function()
15349     {
15350         this.touchViewEl.removeClass('in');
15351
15352         if(this.animate){
15353             var _this = this;
15354             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15355         }else{
15356             this.touchViewEl.setStyle('display', 'none');
15357         }
15358         
15359         if(this._touchViewMask){
15360             this._touchViewMask.removeClass('show');
15361             Roo.get(document.body).removeClass("x-body-masked");
15362         }
15363     },
15364     
15365     setTouchViewValue : function()
15366     {
15367         if(this.multiple){
15368             this.clearItem();
15369         
15370             var _this = this;
15371
15372             Roo.each(this.tickItems, function(o){
15373                 this.addItem(o);
15374             }, this);
15375         }
15376         
15377         this.hideTouchView();
15378     },
15379     
15380     doTouchViewQuery : function()
15381     {
15382         var qe = {
15383             query: '',
15384             forceAll: true,
15385             combo: this,
15386             cancel:false
15387         };
15388         
15389         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15390             return false;
15391         }
15392         
15393         if(!this.alwaysQuery || this.mode == 'local'){
15394             this.onTouchViewLoad();
15395             return;
15396         }
15397         
15398         this.store.load();
15399     },
15400     
15401     onTouchViewBeforeLoad : function(combo,opts)
15402     {
15403         return;
15404     },
15405
15406     // private
15407     onTouchViewLoad : function()
15408     {
15409         if(this.store.getCount() < 1){
15410             this.onTouchViewEmptyResults();
15411             return;
15412         }
15413         
15414         this.clearTouchView();
15415         
15416         var rawValue = this.getRawValue();
15417         
15418         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15419         
15420         this.tickItems = [];
15421         
15422         this.store.data.each(function(d, rowIndex){
15423             var row = this.touchViewListGroup.createChild(template);
15424             
15425             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15426                 row.addClass(d.data.cls);
15427             }
15428             
15429             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15430                 var cfg = {
15431                     data : d.data,
15432                     html : d.data[this.displayField]
15433                 };
15434                 
15435                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15436                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15437                 }
15438             }
15439             row.removeClass('selected');
15440             if(!this.multiple && this.valueField &&
15441                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15442             {
15443                 // radio buttons..
15444                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15445                 row.addClass('selected');
15446             }
15447             
15448             if(this.multiple && this.valueField &&
15449                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15450             {
15451                 
15452                 // checkboxes...
15453                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15454                 this.tickItems.push(d.data);
15455             }
15456             
15457             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15458             
15459         }, this);
15460         
15461         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15462         
15463         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15464
15465         if(this.modalTitle.length){
15466             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15467         }
15468
15469         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15470         
15471         if(this.mobile_restrict_height && listHeight < bodyHeight){
15472             this.touchViewBodyEl.setHeight(listHeight);
15473         }
15474         
15475         var _this = this;
15476         
15477         if(firstChecked && listHeight > bodyHeight){
15478             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15479         }
15480         
15481     },
15482     
15483     onTouchViewLoadException : function()
15484     {
15485         this.hideTouchView();
15486     },
15487     
15488     onTouchViewEmptyResults : function()
15489     {
15490         this.clearTouchView();
15491         
15492         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15493         
15494         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15495         
15496     },
15497     
15498     clearTouchView : function()
15499     {
15500         this.touchViewListGroup.dom.innerHTML = '';
15501     },
15502     
15503     onTouchViewClick : function(e, el, o)
15504     {
15505         e.preventDefault();
15506         
15507         var row = o.row;
15508         var rowIndex = o.rowIndex;
15509         
15510         var r = this.store.getAt(rowIndex);
15511         
15512         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15513             
15514             if(!this.multiple){
15515                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15516                     c.dom.removeAttribute('checked');
15517                 }, this);
15518
15519                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15520
15521                 this.setFromData(r.data);
15522
15523                 var close = this.closeTriggerEl();
15524
15525                 if(close){
15526                     close.show();
15527                 }
15528
15529                 this.hideTouchView();
15530
15531                 this.fireEvent('select', this, r, rowIndex);
15532
15533                 return;
15534             }
15535
15536             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15537                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15538                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15539                 return;
15540             }
15541
15542             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15543             this.addItem(r.data);
15544             this.tickItems.push(r.data);
15545         }
15546     },
15547     
15548     getAutoCreateNativeIOS : function()
15549     {
15550         var cfg = {
15551             cls: 'form-group' //input-group,
15552         };
15553         
15554         var combobox =  {
15555             tag: 'select',
15556             cls : 'roo-ios-select'
15557         };
15558         
15559         if (this.name) {
15560             combobox.name = this.name;
15561         }
15562         
15563         if (this.disabled) {
15564             combobox.disabled = true;
15565         }
15566         
15567         var settings = this;
15568         
15569         ['xs','sm','md','lg'].map(function(size){
15570             if (settings[size]) {
15571                 cfg.cls += ' col-' + size + '-' + settings[size];
15572             }
15573         });
15574         
15575         cfg.cn = combobox;
15576         
15577         return cfg;
15578         
15579     },
15580     
15581     initIOSView : function()
15582     {
15583         this.store.on('load', this.onIOSViewLoad, this);
15584         
15585         return;
15586     },
15587     
15588     onIOSViewLoad : function()
15589     {
15590         if(this.store.getCount() < 1){
15591             return;
15592         }
15593         
15594         this.clearIOSView();
15595         
15596         if(this.allowBlank) {
15597             
15598             var default_text = '-- SELECT --';
15599             
15600             if(this.placeholder.length){
15601                 default_text = this.placeholder;
15602             }
15603             
15604             if(this.emptyTitle.length){
15605                 default_text += ' - ' + this.emptyTitle + ' -';
15606             }
15607             
15608             var opt = this.inputEl().createChild({
15609                 tag: 'option',
15610                 value : 0,
15611                 html : default_text
15612             });
15613             
15614             var o = {};
15615             o[this.valueField] = 0;
15616             o[this.displayField] = default_text;
15617             
15618             this.ios_options.push({
15619                 data : o,
15620                 el : opt
15621             });
15622             
15623         }
15624         
15625         this.store.data.each(function(d, rowIndex){
15626             
15627             var html = '';
15628             
15629             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15630                 html = d.data[this.displayField];
15631             }
15632             
15633             var value = '';
15634             
15635             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15636                 value = d.data[this.valueField];
15637             }
15638             
15639             var option = {
15640                 tag: 'option',
15641                 value : value,
15642                 html : html
15643             };
15644             
15645             if(this.value == d.data[this.valueField]){
15646                 option['selected'] = true;
15647             }
15648             
15649             var opt = this.inputEl().createChild(option);
15650             
15651             this.ios_options.push({
15652                 data : d.data,
15653                 el : opt
15654             });
15655             
15656         }, this);
15657         
15658         this.inputEl().on('change', function(){
15659            this.fireEvent('select', this);
15660         }, this);
15661         
15662     },
15663     
15664     clearIOSView: function()
15665     {
15666         this.inputEl().dom.innerHTML = '';
15667         
15668         this.ios_options = [];
15669     },
15670     
15671     setIOSValue: function(v)
15672     {
15673         this.value = v;
15674         
15675         if(!this.ios_options){
15676             return;
15677         }
15678         
15679         Roo.each(this.ios_options, function(opts){
15680            
15681            opts.el.dom.removeAttribute('selected');
15682            
15683            if(opts.data[this.valueField] != v){
15684                return;
15685            }
15686            
15687            opts.el.dom.setAttribute('selected', true);
15688            
15689         }, this);
15690     }
15691
15692     /** 
15693     * @cfg {Boolean} grow 
15694     * @hide 
15695     */
15696     /** 
15697     * @cfg {Number} growMin 
15698     * @hide 
15699     */
15700     /** 
15701     * @cfg {Number} growMax 
15702     * @hide 
15703     */
15704     /**
15705      * @hide
15706      * @method autoSize
15707      */
15708 });
15709
15710 Roo.apply(Roo.bootstrap.ComboBox,  {
15711     
15712     header : {
15713         tag: 'div',
15714         cls: 'modal-header',
15715         cn: [
15716             {
15717                 tag: 'h4',
15718                 cls: 'modal-title'
15719             }
15720         ]
15721     },
15722     
15723     body : {
15724         tag: 'div',
15725         cls: 'modal-body',
15726         cn: [
15727             {
15728                 tag: 'ul',
15729                 cls: 'list-group'
15730             }
15731         ]
15732     },
15733     
15734     listItemRadio : {
15735         tag: 'li',
15736         cls: 'list-group-item',
15737         cn: [
15738             {
15739                 tag: 'span',
15740                 cls: 'roo-combobox-list-group-item-value'
15741             },
15742             {
15743                 tag: 'div',
15744                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15745                 cn: [
15746                     {
15747                         tag: 'input',
15748                         type: 'radio'
15749                     },
15750                     {
15751                         tag: 'label'
15752                     }
15753                 ]
15754             }
15755         ]
15756     },
15757     
15758     listItemCheckbox : {
15759         tag: 'li',
15760         cls: 'list-group-item',
15761         cn: [
15762             {
15763                 tag: 'span',
15764                 cls: 'roo-combobox-list-group-item-value'
15765             },
15766             {
15767                 tag: 'div',
15768                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15769                 cn: [
15770                     {
15771                         tag: 'input',
15772                         type: 'checkbox'
15773                     },
15774                     {
15775                         tag: 'label'
15776                     }
15777                 ]
15778             }
15779         ]
15780     },
15781     
15782     emptyResult : {
15783         tag: 'div',
15784         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15785     },
15786     
15787     footer : {
15788         tag: 'div',
15789         cls: 'modal-footer',
15790         cn: [
15791             {
15792                 tag: 'div',
15793                 cls: 'row',
15794                 cn: [
15795                     {
15796                         tag: 'div',
15797                         cls: 'col-xs-6 text-left',
15798                         cn: {
15799                             tag: 'button',
15800                             cls: 'btn btn-danger roo-touch-view-cancel',
15801                             html: 'Cancel'
15802                         }
15803                     },
15804                     {
15805                         tag: 'div',
15806                         cls: 'col-xs-6 text-right',
15807                         cn: {
15808                             tag: 'button',
15809                             cls: 'btn btn-success roo-touch-view-ok',
15810                             html: 'OK'
15811                         }
15812                     }
15813                 ]
15814             }
15815         ]
15816         
15817     }
15818 });
15819
15820 Roo.apply(Roo.bootstrap.ComboBox,  {
15821     
15822     touchViewTemplate : {
15823         tag: 'div',
15824         cls: 'modal fade roo-combobox-touch-view',
15825         cn: [
15826             {
15827                 tag: 'div',
15828                 cls: 'modal-dialog',
15829                 style : 'position:fixed', // we have to fix position....
15830                 cn: [
15831                     {
15832                         tag: 'div',
15833                         cls: 'modal-content',
15834                         cn: [
15835                             Roo.bootstrap.ComboBox.header,
15836                             Roo.bootstrap.ComboBox.body,
15837                             Roo.bootstrap.ComboBox.footer
15838                         ]
15839                     }
15840                 ]
15841             }
15842         ]
15843     }
15844 });/*
15845  * Based on:
15846  * Ext JS Library 1.1.1
15847  * Copyright(c) 2006-2007, Ext JS, LLC.
15848  *
15849  * Originally Released Under LGPL - original licence link has changed is not relivant.
15850  *
15851  * Fork - LGPL
15852  * <script type="text/javascript">
15853  */
15854
15855 /**
15856  * @class Roo.View
15857  * @extends Roo.util.Observable
15858  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15859  * This class also supports single and multi selection modes. <br>
15860  * Create a data model bound view:
15861  <pre><code>
15862  var store = new Roo.data.Store(...);
15863
15864  var view = new Roo.View({
15865     el : "my-element",
15866     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15867  
15868     singleSelect: true,
15869     selectedClass: "ydataview-selected",
15870     store: store
15871  });
15872
15873  // listen for node click?
15874  view.on("click", function(vw, index, node, e){
15875  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15876  });
15877
15878  // load XML data
15879  dataModel.load("foobar.xml");
15880  </code></pre>
15881  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15882  * <br><br>
15883  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15884  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15885  * 
15886  * Note: old style constructor is still suported (container, template, config)
15887  * 
15888  * @constructor
15889  * Create a new View
15890  * @param {Object} config The config object
15891  * 
15892  */
15893 Roo.View = function(config, depreciated_tpl, depreciated_config){
15894     
15895     this.parent = false;
15896     
15897     if (typeof(depreciated_tpl) == 'undefined') {
15898         // new way.. - universal constructor.
15899         Roo.apply(this, config);
15900         this.el  = Roo.get(this.el);
15901     } else {
15902         // old format..
15903         this.el  = Roo.get(config);
15904         this.tpl = depreciated_tpl;
15905         Roo.apply(this, depreciated_config);
15906     }
15907     this.wrapEl  = this.el.wrap().wrap();
15908     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15909     
15910     
15911     if(typeof(this.tpl) == "string"){
15912         this.tpl = new Roo.Template(this.tpl);
15913     } else {
15914         // support xtype ctors..
15915         this.tpl = new Roo.factory(this.tpl, Roo);
15916     }
15917     
15918     
15919     this.tpl.compile();
15920     
15921     /** @private */
15922     this.addEvents({
15923         /**
15924          * @event beforeclick
15925          * Fires before a click is processed. Returns false to cancel the default action.
15926          * @param {Roo.View} this
15927          * @param {Number} index The index of the target node
15928          * @param {HTMLElement} node The target node
15929          * @param {Roo.EventObject} e The raw event object
15930          */
15931             "beforeclick" : true,
15932         /**
15933          * @event click
15934          * Fires when a template node is clicked.
15935          * @param {Roo.View} this
15936          * @param {Number} index The index of the target node
15937          * @param {HTMLElement} node The target node
15938          * @param {Roo.EventObject} e The raw event object
15939          */
15940             "click" : true,
15941         /**
15942          * @event dblclick
15943          * Fires when a template node is double clicked.
15944          * @param {Roo.View} this
15945          * @param {Number} index The index of the target node
15946          * @param {HTMLElement} node The target node
15947          * @param {Roo.EventObject} e The raw event object
15948          */
15949             "dblclick" : true,
15950         /**
15951          * @event contextmenu
15952          * Fires when a template node is right clicked.
15953          * @param {Roo.View} this
15954          * @param {Number} index The index of the target node
15955          * @param {HTMLElement} node The target node
15956          * @param {Roo.EventObject} e The raw event object
15957          */
15958             "contextmenu" : true,
15959         /**
15960          * @event selectionchange
15961          * Fires when the selected nodes change.
15962          * @param {Roo.View} this
15963          * @param {Array} selections Array of the selected nodes
15964          */
15965             "selectionchange" : true,
15966     
15967         /**
15968          * @event beforeselect
15969          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15970          * @param {Roo.View} this
15971          * @param {HTMLElement} node The node to be selected
15972          * @param {Array} selections Array of currently selected nodes
15973          */
15974             "beforeselect" : true,
15975         /**
15976          * @event preparedata
15977          * Fires on every row to render, to allow you to change the data.
15978          * @param {Roo.View} this
15979          * @param {Object} data to be rendered (change this)
15980          */
15981           "preparedata" : true
15982           
15983           
15984         });
15985
15986
15987
15988     this.el.on({
15989         "click": this.onClick,
15990         "dblclick": this.onDblClick,
15991         "contextmenu": this.onContextMenu,
15992         scope:this
15993     });
15994
15995     this.selections = [];
15996     this.nodes = [];
15997     this.cmp = new Roo.CompositeElementLite([]);
15998     if(this.store){
15999         this.store = Roo.factory(this.store, Roo.data);
16000         this.setStore(this.store, true);
16001     }
16002     
16003     if ( this.footer && this.footer.xtype) {
16004            
16005          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16006         
16007         this.footer.dataSource = this.store;
16008         this.footer.container = fctr;
16009         this.footer = Roo.factory(this.footer, Roo);
16010         fctr.insertFirst(this.el);
16011         
16012         // this is a bit insane - as the paging toolbar seems to detach the el..
16013 //        dom.parentNode.parentNode.parentNode
16014          // they get detached?
16015     }
16016     
16017     
16018     Roo.View.superclass.constructor.call(this);
16019     
16020     
16021 };
16022
16023 Roo.extend(Roo.View, Roo.util.Observable, {
16024     
16025      /**
16026      * @cfg {Roo.data.Store} store Data store to load data from.
16027      */
16028     store : false,
16029     
16030     /**
16031      * @cfg {String|Roo.Element} el The container element.
16032      */
16033     el : '',
16034     
16035     /**
16036      * @cfg {String|Roo.Template} tpl The template used by this View 
16037      */
16038     tpl : false,
16039     /**
16040      * @cfg {String} dataName the named area of the template to use as the data area
16041      *                          Works with domtemplates roo-name="name"
16042      */
16043     dataName: false,
16044     /**
16045      * @cfg {String} selectedClass The css class to add to selected nodes
16046      */
16047     selectedClass : "x-view-selected",
16048      /**
16049      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16050      */
16051     emptyText : "",
16052     
16053     /**
16054      * @cfg {String} text to display on mask (default Loading)
16055      */
16056     mask : false,
16057     /**
16058      * @cfg {Boolean} multiSelect Allow multiple selection
16059      */
16060     multiSelect : false,
16061     /**
16062      * @cfg {Boolean} singleSelect Allow single selection
16063      */
16064     singleSelect:  false,
16065     
16066     /**
16067      * @cfg {Boolean} toggleSelect - selecting 
16068      */
16069     toggleSelect : false,
16070     
16071     /**
16072      * @cfg {Boolean} tickable - selecting 
16073      */
16074     tickable : false,
16075     
16076     /**
16077      * Returns the element this view is bound to.
16078      * @return {Roo.Element}
16079      */
16080     getEl : function(){
16081         return this.wrapEl;
16082     },
16083     
16084     
16085
16086     /**
16087      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16088      */
16089     refresh : function(){
16090         //Roo.log('refresh');
16091         var t = this.tpl;
16092         
16093         // if we are using something like 'domtemplate', then
16094         // the what gets used is:
16095         // t.applySubtemplate(NAME, data, wrapping data..)
16096         // the outer template then get' applied with
16097         //     the store 'extra data'
16098         // and the body get's added to the
16099         //      roo-name="data" node?
16100         //      <span class='roo-tpl-{name}'></span> ?????
16101         
16102         
16103         
16104         this.clearSelections();
16105         this.el.update("");
16106         var html = [];
16107         var records = this.store.getRange();
16108         if(records.length < 1) {
16109             
16110             // is this valid??  = should it render a template??
16111             
16112             this.el.update(this.emptyText);
16113             return;
16114         }
16115         var el = this.el;
16116         if (this.dataName) {
16117             this.el.update(t.apply(this.store.meta)); //????
16118             el = this.el.child('.roo-tpl-' + this.dataName);
16119         }
16120         
16121         for(var i = 0, len = records.length; i < len; i++){
16122             var data = this.prepareData(records[i].data, i, records[i]);
16123             this.fireEvent("preparedata", this, data, i, records[i]);
16124             
16125             var d = Roo.apply({}, data);
16126             
16127             if(this.tickable){
16128                 Roo.apply(d, {'roo-id' : Roo.id()});
16129                 
16130                 var _this = this;
16131             
16132                 Roo.each(this.parent.item, function(item){
16133                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16134                         return;
16135                     }
16136                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16137                 });
16138             }
16139             
16140             html[html.length] = Roo.util.Format.trim(
16141                 this.dataName ?
16142                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16143                     t.apply(d)
16144             );
16145         }
16146         
16147         
16148         
16149         el.update(html.join(""));
16150         this.nodes = el.dom.childNodes;
16151         this.updateIndexes(0);
16152     },
16153     
16154
16155     /**
16156      * Function to override to reformat the data that is sent to
16157      * the template for each node.
16158      * DEPRICATED - use the preparedata event handler.
16159      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16160      * a JSON object for an UpdateManager bound view).
16161      */
16162     prepareData : function(data, index, record)
16163     {
16164         this.fireEvent("preparedata", this, data, index, record);
16165         return data;
16166     },
16167
16168     onUpdate : function(ds, record){
16169         // Roo.log('on update');   
16170         this.clearSelections();
16171         var index = this.store.indexOf(record);
16172         var n = this.nodes[index];
16173         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16174         n.parentNode.removeChild(n);
16175         this.updateIndexes(index, index);
16176     },
16177
16178     
16179     
16180 // --------- FIXME     
16181     onAdd : function(ds, records, index)
16182     {
16183         //Roo.log(['on Add', ds, records, index] );        
16184         this.clearSelections();
16185         if(this.nodes.length == 0){
16186             this.refresh();
16187             return;
16188         }
16189         var n = this.nodes[index];
16190         for(var i = 0, len = records.length; i < len; i++){
16191             var d = this.prepareData(records[i].data, i, records[i]);
16192             if(n){
16193                 this.tpl.insertBefore(n, d);
16194             }else{
16195                 
16196                 this.tpl.append(this.el, d);
16197             }
16198         }
16199         this.updateIndexes(index);
16200     },
16201
16202     onRemove : function(ds, record, index){
16203        // Roo.log('onRemove');
16204         this.clearSelections();
16205         var el = this.dataName  ?
16206             this.el.child('.roo-tpl-' + this.dataName) :
16207             this.el; 
16208         
16209         el.dom.removeChild(this.nodes[index]);
16210         this.updateIndexes(index);
16211     },
16212
16213     /**
16214      * Refresh an individual node.
16215      * @param {Number} index
16216      */
16217     refreshNode : function(index){
16218         this.onUpdate(this.store, this.store.getAt(index));
16219     },
16220
16221     updateIndexes : function(startIndex, endIndex){
16222         var ns = this.nodes;
16223         startIndex = startIndex || 0;
16224         endIndex = endIndex || ns.length - 1;
16225         for(var i = startIndex; i <= endIndex; i++){
16226             ns[i].nodeIndex = i;
16227         }
16228     },
16229
16230     /**
16231      * Changes the data store this view uses and refresh the view.
16232      * @param {Store} store
16233      */
16234     setStore : function(store, initial){
16235         if(!initial && this.store){
16236             this.store.un("datachanged", this.refresh);
16237             this.store.un("add", this.onAdd);
16238             this.store.un("remove", this.onRemove);
16239             this.store.un("update", this.onUpdate);
16240             this.store.un("clear", this.refresh);
16241             this.store.un("beforeload", this.onBeforeLoad);
16242             this.store.un("load", this.onLoad);
16243             this.store.un("loadexception", this.onLoad);
16244         }
16245         if(store){
16246           
16247             store.on("datachanged", this.refresh, this);
16248             store.on("add", this.onAdd, this);
16249             store.on("remove", this.onRemove, this);
16250             store.on("update", this.onUpdate, this);
16251             store.on("clear", this.refresh, this);
16252             store.on("beforeload", this.onBeforeLoad, this);
16253             store.on("load", this.onLoad, this);
16254             store.on("loadexception", this.onLoad, this);
16255         }
16256         
16257         if(store){
16258             this.refresh();
16259         }
16260     },
16261     /**
16262      * onbeforeLoad - masks the loading area.
16263      *
16264      */
16265     onBeforeLoad : function(store,opts)
16266     {
16267          //Roo.log('onBeforeLoad');   
16268         if (!opts.add) {
16269             this.el.update("");
16270         }
16271         this.el.mask(this.mask ? this.mask : "Loading" ); 
16272     },
16273     onLoad : function ()
16274     {
16275         this.el.unmask();
16276     },
16277     
16278
16279     /**
16280      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16281      * @param {HTMLElement} node
16282      * @return {HTMLElement} The template node
16283      */
16284     findItemFromChild : function(node){
16285         var el = this.dataName  ?
16286             this.el.child('.roo-tpl-' + this.dataName,true) :
16287             this.el.dom; 
16288         
16289         if(!node || node.parentNode == el){
16290                     return node;
16291             }
16292             var p = node.parentNode;
16293             while(p && p != el){
16294             if(p.parentNode == el){
16295                 return p;
16296             }
16297             p = p.parentNode;
16298         }
16299             return null;
16300     },
16301
16302     /** @ignore */
16303     onClick : function(e){
16304         var item = this.findItemFromChild(e.getTarget());
16305         if(item){
16306             var index = this.indexOf(item);
16307             if(this.onItemClick(item, index, e) !== false){
16308                 this.fireEvent("click", this, index, item, e);
16309             }
16310         }else{
16311             this.clearSelections();
16312         }
16313     },
16314
16315     /** @ignore */
16316     onContextMenu : function(e){
16317         var item = this.findItemFromChild(e.getTarget());
16318         if(item){
16319             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16320         }
16321     },
16322
16323     /** @ignore */
16324     onDblClick : function(e){
16325         var item = this.findItemFromChild(e.getTarget());
16326         if(item){
16327             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16328         }
16329     },
16330
16331     onItemClick : function(item, index, e)
16332     {
16333         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16334             return false;
16335         }
16336         if (this.toggleSelect) {
16337             var m = this.isSelected(item) ? 'unselect' : 'select';
16338             //Roo.log(m);
16339             var _t = this;
16340             _t[m](item, true, false);
16341             return true;
16342         }
16343         if(this.multiSelect || this.singleSelect){
16344             if(this.multiSelect && e.shiftKey && this.lastSelection){
16345                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16346             }else{
16347                 this.select(item, this.multiSelect && e.ctrlKey);
16348                 this.lastSelection = item;
16349             }
16350             
16351             if(!this.tickable){
16352                 e.preventDefault();
16353             }
16354             
16355         }
16356         return true;
16357     },
16358
16359     /**
16360      * Get the number of selected nodes.
16361      * @return {Number}
16362      */
16363     getSelectionCount : function(){
16364         return this.selections.length;
16365     },
16366
16367     /**
16368      * Get the currently selected nodes.
16369      * @return {Array} An array of HTMLElements
16370      */
16371     getSelectedNodes : function(){
16372         return this.selections;
16373     },
16374
16375     /**
16376      * Get the indexes of the selected nodes.
16377      * @return {Array}
16378      */
16379     getSelectedIndexes : function(){
16380         var indexes = [], s = this.selections;
16381         for(var i = 0, len = s.length; i < len; i++){
16382             indexes.push(s[i].nodeIndex);
16383         }
16384         return indexes;
16385     },
16386
16387     /**
16388      * Clear all selections
16389      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16390      */
16391     clearSelections : function(suppressEvent){
16392         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16393             this.cmp.elements = this.selections;
16394             this.cmp.removeClass(this.selectedClass);
16395             this.selections = [];
16396             if(!suppressEvent){
16397                 this.fireEvent("selectionchange", this, this.selections);
16398             }
16399         }
16400     },
16401
16402     /**
16403      * Returns true if the passed node is selected
16404      * @param {HTMLElement/Number} node The node or node index
16405      * @return {Boolean}
16406      */
16407     isSelected : function(node){
16408         var s = this.selections;
16409         if(s.length < 1){
16410             return false;
16411         }
16412         node = this.getNode(node);
16413         return s.indexOf(node) !== -1;
16414     },
16415
16416     /**
16417      * Selects nodes.
16418      * @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
16419      * @param {Boolean} keepExisting (optional) true to keep existing selections
16420      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16421      */
16422     select : function(nodeInfo, keepExisting, suppressEvent){
16423         if(nodeInfo instanceof Array){
16424             if(!keepExisting){
16425                 this.clearSelections(true);
16426             }
16427             for(var i = 0, len = nodeInfo.length; i < len; i++){
16428                 this.select(nodeInfo[i], true, true);
16429             }
16430             return;
16431         } 
16432         var node = this.getNode(nodeInfo);
16433         if(!node || this.isSelected(node)){
16434             return; // already selected.
16435         }
16436         if(!keepExisting){
16437             this.clearSelections(true);
16438         }
16439         
16440         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16441             Roo.fly(node).addClass(this.selectedClass);
16442             this.selections.push(node);
16443             if(!suppressEvent){
16444                 this.fireEvent("selectionchange", this, this.selections);
16445             }
16446         }
16447         
16448         
16449     },
16450       /**
16451      * Unselects nodes.
16452      * @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
16453      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16454      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16455      */
16456     unselect : function(nodeInfo, keepExisting, suppressEvent)
16457     {
16458         if(nodeInfo instanceof Array){
16459             Roo.each(this.selections, function(s) {
16460                 this.unselect(s, nodeInfo);
16461             }, this);
16462             return;
16463         }
16464         var node = this.getNode(nodeInfo);
16465         if(!node || !this.isSelected(node)){
16466             //Roo.log("not selected");
16467             return; // not selected.
16468         }
16469         // fireevent???
16470         var ns = [];
16471         Roo.each(this.selections, function(s) {
16472             if (s == node ) {
16473                 Roo.fly(node).removeClass(this.selectedClass);
16474
16475                 return;
16476             }
16477             ns.push(s);
16478         },this);
16479         
16480         this.selections= ns;
16481         this.fireEvent("selectionchange", this, this.selections);
16482     },
16483
16484     /**
16485      * Gets a template node.
16486      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16487      * @return {HTMLElement} The node or null if it wasn't found
16488      */
16489     getNode : function(nodeInfo){
16490         if(typeof nodeInfo == "string"){
16491             return document.getElementById(nodeInfo);
16492         }else if(typeof nodeInfo == "number"){
16493             return this.nodes[nodeInfo];
16494         }
16495         return nodeInfo;
16496     },
16497
16498     /**
16499      * Gets a range template nodes.
16500      * @param {Number} startIndex
16501      * @param {Number} endIndex
16502      * @return {Array} An array of nodes
16503      */
16504     getNodes : function(start, end){
16505         var ns = this.nodes;
16506         start = start || 0;
16507         end = typeof end == "undefined" ? ns.length - 1 : end;
16508         var nodes = [];
16509         if(start <= end){
16510             for(var i = start; i <= end; i++){
16511                 nodes.push(ns[i]);
16512             }
16513         } else{
16514             for(var i = start; i >= end; i--){
16515                 nodes.push(ns[i]);
16516             }
16517         }
16518         return nodes;
16519     },
16520
16521     /**
16522      * Finds the index of the passed node
16523      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16524      * @return {Number} The index of the node or -1
16525      */
16526     indexOf : function(node){
16527         node = this.getNode(node);
16528         if(typeof node.nodeIndex == "number"){
16529             return node.nodeIndex;
16530         }
16531         var ns = this.nodes;
16532         for(var i = 0, len = ns.length; i < len; i++){
16533             if(ns[i] == node){
16534                 return i;
16535             }
16536         }
16537         return -1;
16538     }
16539 });
16540 /*
16541  * - LGPL
16542  *
16543  * based on jquery fullcalendar
16544  * 
16545  */
16546
16547 Roo.bootstrap = Roo.bootstrap || {};
16548 /**
16549  * @class Roo.bootstrap.Calendar
16550  * @extends Roo.bootstrap.Component
16551  * Bootstrap Calendar class
16552  * @cfg {Boolean} loadMask (true|false) default false
16553  * @cfg {Object} header generate the user specific header of the calendar, default false
16554
16555  * @constructor
16556  * Create a new Container
16557  * @param {Object} config The config object
16558  */
16559
16560
16561
16562 Roo.bootstrap.Calendar = function(config){
16563     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16564      this.addEvents({
16565         /**
16566              * @event select
16567              * Fires when a date is selected
16568              * @param {DatePicker} this
16569              * @param {Date} date The selected date
16570              */
16571         'select': true,
16572         /**
16573              * @event monthchange
16574              * Fires when the displayed month changes 
16575              * @param {DatePicker} this
16576              * @param {Date} date The selected month
16577              */
16578         'monthchange': true,
16579         /**
16580              * @event evententer
16581              * Fires when mouse over an event
16582              * @param {Calendar} this
16583              * @param {event} Event
16584              */
16585         'evententer': true,
16586         /**
16587              * @event eventleave
16588              * Fires when the mouse leaves an
16589              * @param {Calendar} this
16590              * @param {event}
16591              */
16592         'eventleave': true,
16593         /**
16594              * @event eventclick
16595              * Fires when the mouse click an
16596              * @param {Calendar} this
16597              * @param {event}
16598              */
16599         'eventclick': true
16600         
16601     });
16602
16603 };
16604
16605 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16606     
16607      /**
16608      * @cfg {Number} startDay
16609      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16610      */
16611     startDay : 0,
16612     
16613     loadMask : false,
16614     
16615     header : false,
16616       
16617     getAutoCreate : function(){
16618         
16619         
16620         var fc_button = function(name, corner, style, content ) {
16621             return Roo.apply({},{
16622                 tag : 'span',
16623                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16624                          (corner.length ?
16625                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16626                             ''
16627                         ),
16628                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16629                 unselectable: 'on'
16630             });
16631         };
16632         
16633         var header = {};
16634         
16635         if(!this.header){
16636             header = {
16637                 tag : 'table',
16638                 cls : 'fc-header',
16639                 style : 'width:100%',
16640                 cn : [
16641                     {
16642                         tag: 'tr',
16643                         cn : [
16644                             {
16645                                 tag : 'td',
16646                                 cls : 'fc-header-left',
16647                                 cn : [
16648                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16649                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16650                                     { tag: 'span', cls: 'fc-header-space' },
16651                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16652
16653
16654                                 ]
16655                             },
16656
16657                             {
16658                                 tag : 'td',
16659                                 cls : 'fc-header-center',
16660                                 cn : [
16661                                     {
16662                                         tag: 'span',
16663                                         cls: 'fc-header-title',
16664                                         cn : {
16665                                             tag: 'H2',
16666                                             html : 'month / year'
16667                                         }
16668                                     }
16669
16670                                 ]
16671                             },
16672                             {
16673                                 tag : 'td',
16674                                 cls : 'fc-header-right',
16675                                 cn : [
16676                               /*      fc_button('month', 'left', '', 'month' ),
16677                                     fc_button('week', '', '', 'week' ),
16678                                     fc_button('day', 'right', '', 'day' )
16679                                 */    
16680
16681                                 ]
16682                             }
16683
16684                         ]
16685                     }
16686                 ]
16687             };
16688         }
16689         
16690         header = this.header;
16691         
16692        
16693         var cal_heads = function() {
16694             var ret = [];
16695             // fixme - handle this.
16696             
16697             for (var i =0; i < Date.dayNames.length; i++) {
16698                 var d = Date.dayNames[i];
16699                 ret.push({
16700                     tag: 'th',
16701                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16702                     html : d.substring(0,3)
16703                 });
16704                 
16705             }
16706             ret[0].cls += ' fc-first';
16707             ret[6].cls += ' fc-last';
16708             return ret;
16709         };
16710         var cal_cell = function(n) {
16711             return  {
16712                 tag: 'td',
16713                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16714                 cn : [
16715                     {
16716                         cn : [
16717                             {
16718                                 cls: 'fc-day-number',
16719                                 html: 'D'
16720                             },
16721                             {
16722                                 cls: 'fc-day-content',
16723                              
16724                                 cn : [
16725                                      {
16726                                         style: 'position: relative;' // height: 17px;
16727                                     }
16728                                 ]
16729                             }
16730                             
16731                             
16732                         ]
16733                     }
16734                 ]
16735                 
16736             }
16737         };
16738         var cal_rows = function() {
16739             
16740             var ret = [];
16741             for (var r = 0; r < 6; r++) {
16742                 var row= {
16743                     tag : 'tr',
16744                     cls : 'fc-week',
16745                     cn : []
16746                 };
16747                 
16748                 for (var i =0; i < Date.dayNames.length; i++) {
16749                     var d = Date.dayNames[i];
16750                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16751
16752                 }
16753                 row.cn[0].cls+=' fc-first';
16754                 row.cn[0].cn[0].style = 'min-height:90px';
16755                 row.cn[6].cls+=' fc-last';
16756                 ret.push(row);
16757                 
16758             }
16759             ret[0].cls += ' fc-first';
16760             ret[4].cls += ' fc-prev-last';
16761             ret[5].cls += ' fc-last';
16762             return ret;
16763             
16764         };
16765         
16766         var cal_table = {
16767             tag: 'table',
16768             cls: 'fc-border-separate',
16769             style : 'width:100%',
16770             cellspacing  : 0,
16771             cn : [
16772                 { 
16773                     tag: 'thead',
16774                     cn : [
16775                         { 
16776                             tag: 'tr',
16777                             cls : 'fc-first fc-last',
16778                             cn : cal_heads()
16779                         }
16780                     ]
16781                 },
16782                 { 
16783                     tag: 'tbody',
16784                     cn : cal_rows()
16785                 }
16786                   
16787             ]
16788         };
16789          
16790          var cfg = {
16791             cls : 'fc fc-ltr',
16792             cn : [
16793                 header,
16794                 {
16795                     cls : 'fc-content',
16796                     style : "position: relative;",
16797                     cn : [
16798                         {
16799                             cls : 'fc-view fc-view-month fc-grid',
16800                             style : 'position: relative',
16801                             unselectable : 'on',
16802                             cn : [
16803                                 {
16804                                     cls : 'fc-event-container',
16805                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16806                                 },
16807                                 cal_table
16808                             ]
16809                         }
16810                     ]
16811     
16812                 }
16813            ] 
16814             
16815         };
16816         
16817          
16818         
16819         return cfg;
16820     },
16821     
16822     
16823     initEvents : function()
16824     {
16825         if(!this.store){
16826             throw "can not find store for calendar";
16827         }
16828         
16829         var mark = {
16830             tag: "div",
16831             cls:"x-dlg-mask",
16832             style: "text-align:center",
16833             cn: [
16834                 {
16835                     tag: "div",
16836                     style: "background-color:white;width:50%;margin:250 auto",
16837                     cn: [
16838                         {
16839                             tag: "img",
16840                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16841                         },
16842                         {
16843                             tag: "span",
16844                             html: "Loading"
16845                         }
16846                         
16847                     ]
16848                 }
16849             ]
16850         };
16851         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16852         
16853         var size = this.el.select('.fc-content', true).first().getSize();
16854         this.maskEl.setSize(size.width, size.height);
16855         this.maskEl.enableDisplayMode("block");
16856         if(!this.loadMask){
16857             this.maskEl.hide();
16858         }
16859         
16860         this.store = Roo.factory(this.store, Roo.data);
16861         this.store.on('load', this.onLoad, this);
16862         this.store.on('beforeload', this.onBeforeLoad, this);
16863         
16864         this.resize();
16865         
16866         this.cells = this.el.select('.fc-day',true);
16867         //Roo.log(this.cells);
16868         this.textNodes = this.el.query('.fc-day-number');
16869         this.cells.addClassOnOver('fc-state-hover');
16870         
16871         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16872         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16873         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16874         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16875         
16876         this.on('monthchange', this.onMonthChange, this);
16877         
16878         this.update(new Date().clearTime());
16879     },
16880     
16881     resize : function() {
16882         var sz  = this.el.getSize();
16883         
16884         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16885         this.el.select('.fc-day-content div',true).setHeight(34);
16886     },
16887     
16888     
16889     // private
16890     showPrevMonth : function(e){
16891         this.update(this.activeDate.add("mo", -1));
16892     },
16893     showToday : function(e){
16894         this.update(new Date().clearTime());
16895     },
16896     // private
16897     showNextMonth : function(e){
16898         this.update(this.activeDate.add("mo", 1));
16899     },
16900
16901     // private
16902     showPrevYear : function(){
16903         this.update(this.activeDate.add("y", -1));
16904     },
16905
16906     // private
16907     showNextYear : function(){
16908         this.update(this.activeDate.add("y", 1));
16909     },
16910
16911     
16912    // private
16913     update : function(date)
16914     {
16915         var vd = this.activeDate;
16916         this.activeDate = date;
16917 //        if(vd && this.el){
16918 //            var t = date.getTime();
16919 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16920 //                Roo.log('using add remove');
16921 //                
16922 //                this.fireEvent('monthchange', this, date);
16923 //                
16924 //                this.cells.removeClass("fc-state-highlight");
16925 //                this.cells.each(function(c){
16926 //                   if(c.dateValue == t){
16927 //                       c.addClass("fc-state-highlight");
16928 //                       setTimeout(function(){
16929 //                            try{c.dom.firstChild.focus();}catch(e){}
16930 //                       }, 50);
16931 //                       return false;
16932 //                   }
16933 //                   return true;
16934 //                });
16935 //                return;
16936 //            }
16937 //        }
16938         
16939         var days = date.getDaysInMonth();
16940         
16941         var firstOfMonth = date.getFirstDateOfMonth();
16942         var startingPos = firstOfMonth.getDay()-this.startDay;
16943         
16944         if(startingPos < this.startDay){
16945             startingPos += 7;
16946         }
16947         
16948         var pm = date.add(Date.MONTH, -1);
16949         var prevStart = pm.getDaysInMonth()-startingPos;
16950 //        
16951         this.cells = this.el.select('.fc-day',true);
16952         this.textNodes = this.el.query('.fc-day-number');
16953         this.cells.addClassOnOver('fc-state-hover');
16954         
16955         var cells = this.cells.elements;
16956         var textEls = this.textNodes;
16957         
16958         Roo.each(cells, function(cell){
16959             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16960         });
16961         
16962         days += startingPos;
16963
16964         // convert everything to numbers so it's fast
16965         var day = 86400000;
16966         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16967         //Roo.log(d);
16968         //Roo.log(pm);
16969         //Roo.log(prevStart);
16970         
16971         var today = new Date().clearTime().getTime();
16972         var sel = date.clearTime().getTime();
16973         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16974         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16975         var ddMatch = this.disabledDatesRE;
16976         var ddText = this.disabledDatesText;
16977         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16978         var ddaysText = this.disabledDaysText;
16979         var format = this.format;
16980         
16981         var setCellClass = function(cal, cell){
16982             cell.row = 0;
16983             cell.events = [];
16984             cell.more = [];
16985             //Roo.log('set Cell Class');
16986             cell.title = "";
16987             var t = d.getTime();
16988             
16989             //Roo.log(d);
16990             
16991             cell.dateValue = t;
16992             if(t == today){
16993                 cell.className += " fc-today";
16994                 cell.className += " fc-state-highlight";
16995                 cell.title = cal.todayText;
16996             }
16997             if(t == sel){
16998                 // disable highlight in other month..
16999                 //cell.className += " fc-state-highlight";
17000                 
17001             }
17002             // disabling
17003             if(t < min) {
17004                 cell.className = " fc-state-disabled";
17005                 cell.title = cal.minText;
17006                 return;
17007             }
17008             if(t > max) {
17009                 cell.className = " fc-state-disabled";
17010                 cell.title = cal.maxText;
17011                 return;
17012             }
17013             if(ddays){
17014                 if(ddays.indexOf(d.getDay()) != -1){
17015                     cell.title = ddaysText;
17016                     cell.className = " fc-state-disabled";
17017                 }
17018             }
17019             if(ddMatch && format){
17020                 var fvalue = d.dateFormat(format);
17021                 if(ddMatch.test(fvalue)){
17022                     cell.title = ddText.replace("%0", fvalue);
17023                     cell.className = " fc-state-disabled";
17024                 }
17025             }
17026             
17027             if (!cell.initialClassName) {
17028                 cell.initialClassName = cell.dom.className;
17029             }
17030             
17031             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17032         };
17033
17034         var i = 0;
17035         
17036         for(; i < startingPos; i++) {
17037             textEls[i].innerHTML = (++prevStart);
17038             d.setDate(d.getDate()+1);
17039             
17040             cells[i].className = "fc-past fc-other-month";
17041             setCellClass(this, cells[i]);
17042         }
17043         
17044         var intDay = 0;
17045         
17046         for(; i < days; i++){
17047             intDay = i - startingPos + 1;
17048             textEls[i].innerHTML = (intDay);
17049             d.setDate(d.getDate()+1);
17050             
17051             cells[i].className = ''; // "x-date-active";
17052             setCellClass(this, cells[i]);
17053         }
17054         var extraDays = 0;
17055         
17056         for(; i < 42; i++) {
17057             textEls[i].innerHTML = (++extraDays);
17058             d.setDate(d.getDate()+1);
17059             
17060             cells[i].className = "fc-future fc-other-month";
17061             setCellClass(this, cells[i]);
17062         }
17063         
17064         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17065         
17066         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17067         
17068         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17069         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17070         
17071         if(totalRows != 6){
17072             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17073             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17074         }
17075         
17076         this.fireEvent('monthchange', this, date);
17077         
17078         
17079         /*
17080         if(!this.internalRender){
17081             var main = this.el.dom.firstChild;
17082             var w = main.offsetWidth;
17083             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17084             Roo.fly(main).setWidth(w);
17085             this.internalRender = true;
17086             // opera does not respect the auto grow header center column
17087             // then, after it gets a width opera refuses to recalculate
17088             // without a second pass
17089             if(Roo.isOpera && !this.secondPass){
17090                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17091                 this.secondPass = true;
17092                 this.update.defer(10, this, [date]);
17093             }
17094         }
17095         */
17096         
17097     },
17098     
17099     findCell : function(dt) {
17100         dt = dt.clearTime().getTime();
17101         var ret = false;
17102         this.cells.each(function(c){
17103             //Roo.log("check " +c.dateValue + '?=' + dt);
17104             if(c.dateValue == dt){
17105                 ret = c;
17106                 return false;
17107             }
17108             return true;
17109         });
17110         
17111         return ret;
17112     },
17113     
17114     findCells : function(ev) {
17115         var s = ev.start.clone().clearTime().getTime();
17116        // Roo.log(s);
17117         var e= ev.end.clone().clearTime().getTime();
17118        // Roo.log(e);
17119         var ret = [];
17120         this.cells.each(function(c){
17121              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17122             
17123             if(c.dateValue > e){
17124                 return ;
17125             }
17126             if(c.dateValue < s){
17127                 return ;
17128             }
17129             ret.push(c);
17130         });
17131         
17132         return ret;    
17133     },
17134     
17135 //    findBestRow: function(cells)
17136 //    {
17137 //        var ret = 0;
17138 //        
17139 //        for (var i =0 ; i < cells.length;i++) {
17140 //            ret  = Math.max(cells[i].rows || 0,ret);
17141 //        }
17142 //        return ret;
17143 //        
17144 //    },
17145     
17146     
17147     addItem : function(ev)
17148     {
17149         // look for vertical location slot in
17150         var cells = this.findCells(ev);
17151         
17152 //        ev.row = this.findBestRow(cells);
17153         
17154         // work out the location.
17155         
17156         var crow = false;
17157         var rows = [];
17158         for(var i =0; i < cells.length; i++) {
17159             
17160             cells[i].row = cells[0].row;
17161             
17162             if(i == 0){
17163                 cells[i].row = cells[i].row + 1;
17164             }
17165             
17166             if (!crow) {
17167                 crow = {
17168                     start : cells[i],
17169                     end :  cells[i]
17170                 };
17171                 continue;
17172             }
17173             if (crow.start.getY() == cells[i].getY()) {
17174                 // on same row.
17175                 crow.end = cells[i];
17176                 continue;
17177             }
17178             // different row.
17179             rows.push(crow);
17180             crow = {
17181                 start: cells[i],
17182                 end : cells[i]
17183             };
17184             
17185         }
17186         
17187         rows.push(crow);
17188         ev.els = [];
17189         ev.rows = rows;
17190         ev.cells = cells;
17191         
17192         cells[0].events.push(ev);
17193         
17194         this.calevents.push(ev);
17195     },
17196     
17197     clearEvents: function() {
17198         
17199         if(!this.calevents){
17200             return;
17201         }
17202         
17203         Roo.each(this.cells.elements, function(c){
17204             c.row = 0;
17205             c.events = [];
17206             c.more = [];
17207         });
17208         
17209         Roo.each(this.calevents, function(e) {
17210             Roo.each(e.els, function(el) {
17211                 el.un('mouseenter' ,this.onEventEnter, this);
17212                 el.un('mouseleave' ,this.onEventLeave, this);
17213                 el.remove();
17214             },this);
17215         },this);
17216         
17217         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17218             e.remove();
17219         });
17220         
17221     },
17222     
17223     renderEvents: function()
17224     {   
17225         var _this = this;
17226         
17227         this.cells.each(function(c) {
17228             
17229             if(c.row < 5){
17230                 return;
17231             }
17232             
17233             var ev = c.events;
17234             
17235             var r = 4;
17236             if(c.row != c.events.length){
17237                 r = 4 - (4 - (c.row - c.events.length));
17238             }
17239             
17240             c.events = ev.slice(0, r);
17241             c.more = ev.slice(r);
17242             
17243             if(c.more.length && c.more.length == 1){
17244                 c.events.push(c.more.pop());
17245             }
17246             
17247             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17248             
17249         });
17250             
17251         this.cells.each(function(c) {
17252             
17253             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17254             
17255             
17256             for (var e = 0; e < c.events.length; e++){
17257                 var ev = c.events[e];
17258                 var rows = ev.rows;
17259                 
17260                 for(var i = 0; i < rows.length; i++) {
17261                 
17262                     // how many rows should it span..
17263
17264                     var  cfg = {
17265                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17266                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17267
17268                         unselectable : "on",
17269                         cn : [
17270                             {
17271                                 cls: 'fc-event-inner',
17272                                 cn : [
17273     //                                {
17274     //                                  tag:'span',
17275     //                                  cls: 'fc-event-time',
17276     //                                  html : cells.length > 1 ? '' : ev.time
17277     //                                },
17278                                     {
17279                                       tag:'span',
17280                                       cls: 'fc-event-title',
17281                                       html : String.format('{0}', ev.title)
17282                                     }
17283
17284
17285                                 ]
17286                             },
17287                             {
17288                                 cls: 'ui-resizable-handle ui-resizable-e',
17289                                 html : '&nbsp;&nbsp;&nbsp'
17290                             }
17291
17292                         ]
17293                     };
17294
17295                     if (i == 0) {
17296                         cfg.cls += ' fc-event-start';
17297                     }
17298                     if ((i+1) == rows.length) {
17299                         cfg.cls += ' fc-event-end';
17300                     }
17301
17302                     var ctr = _this.el.select('.fc-event-container',true).first();
17303                     var cg = ctr.createChild(cfg);
17304
17305                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17306                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17307
17308                     var r = (c.more.length) ? 1 : 0;
17309                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17310                     cg.setWidth(ebox.right - sbox.x -2);
17311
17312                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17313                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17314                     cg.on('click', _this.onEventClick, _this, ev);
17315
17316                     ev.els.push(cg);
17317                     
17318                 }
17319                 
17320             }
17321             
17322             
17323             if(c.more.length){
17324                 var  cfg = {
17325                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17326                     style : 'position: absolute',
17327                     unselectable : "on",
17328                     cn : [
17329                         {
17330                             cls: 'fc-event-inner',
17331                             cn : [
17332                                 {
17333                                   tag:'span',
17334                                   cls: 'fc-event-title',
17335                                   html : 'More'
17336                                 }
17337
17338
17339                             ]
17340                         },
17341                         {
17342                             cls: 'ui-resizable-handle ui-resizable-e',
17343                             html : '&nbsp;&nbsp;&nbsp'
17344                         }
17345
17346                     ]
17347                 };
17348
17349                 var ctr = _this.el.select('.fc-event-container',true).first();
17350                 var cg = ctr.createChild(cfg);
17351
17352                 var sbox = c.select('.fc-day-content',true).first().getBox();
17353                 var ebox = c.select('.fc-day-content',true).first().getBox();
17354                 //Roo.log(cg);
17355                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17356                 cg.setWidth(ebox.right - sbox.x -2);
17357
17358                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17359                 
17360             }
17361             
17362         });
17363         
17364         
17365         
17366     },
17367     
17368     onEventEnter: function (e, el,event,d) {
17369         this.fireEvent('evententer', this, el, event);
17370     },
17371     
17372     onEventLeave: function (e, el,event,d) {
17373         this.fireEvent('eventleave', this, el, event);
17374     },
17375     
17376     onEventClick: function (e, el,event,d) {
17377         this.fireEvent('eventclick', this, el, event);
17378     },
17379     
17380     onMonthChange: function () {
17381         this.store.load();
17382     },
17383     
17384     onMoreEventClick: function(e, el, more)
17385     {
17386         var _this = this;
17387         
17388         this.calpopover.placement = 'right';
17389         this.calpopover.setTitle('More');
17390         
17391         this.calpopover.setContent('');
17392         
17393         var ctr = this.calpopover.el.select('.popover-content', true).first();
17394         
17395         Roo.each(more, function(m){
17396             var cfg = {
17397                 cls : 'fc-event-hori fc-event-draggable',
17398                 html : m.title
17399             };
17400             var cg = ctr.createChild(cfg);
17401             
17402             cg.on('click', _this.onEventClick, _this, m);
17403         });
17404         
17405         this.calpopover.show(el);
17406         
17407         
17408     },
17409     
17410     onLoad: function () 
17411     {   
17412         this.calevents = [];
17413         var cal = this;
17414         
17415         if(this.store.getCount() > 0){
17416             this.store.data.each(function(d){
17417                cal.addItem({
17418                     id : d.data.id,
17419                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17420                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17421                     time : d.data.start_time,
17422                     title : d.data.title,
17423                     description : d.data.description,
17424                     venue : d.data.venue
17425                 });
17426             });
17427         }
17428         
17429         this.renderEvents();
17430         
17431         if(this.calevents.length && this.loadMask){
17432             this.maskEl.hide();
17433         }
17434     },
17435     
17436     onBeforeLoad: function()
17437     {
17438         this.clearEvents();
17439         if(this.loadMask){
17440             this.maskEl.show();
17441         }
17442     }
17443 });
17444
17445  
17446  /*
17447  * - LGPL
17448  *
17449  * element
17450  * 
17451  */
17452
17453 /**
17454  * @class Roo.bootstrap.Popover
17455  * @extends Roo.bootstrap.Component
17456  * Bootstrap Popover class
17457  * @cfg {String} html contents of the popover   (or false to use children..)
17458  * @cfg {String} title of popover (or false to hide)
17459  * @cfg {String} placement how it is placed
17460  * @cfg {String} trigger click || hover (or false to trigger manually)
17461  * @cfg {String} over what (parent or false to trigger manually.)
17462  * @cfg {Number} delay - delay before showing
17463  
17464  * @constructor
17465  * Create a new Popover
17466  * @param {Object} config The config object
17467  */
17468
17469 Roo.bootstrap.Popover = function(config){
17470     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17471     
17472     this.addEvents({
17473         // raw events
17474          /**
17475          * @event show
17476          * After the popover show
17477          * 
17478          * @param {Roo.bootstrap.Popover} this
17479          */
17480         "show" : true,
17481         /**
17482          * @event hide
17483          * After the popover hide
17484          * 
17485          * @param {Roo.bootstrap.Popover} this
17486          */
17487         "hide" : true
17488     });
17489 };
17490
17491 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17492     
17493     title: 'Fill in a title',
17494     html: false,
17495     
17496     placement : 'right',
17497     trigger : 'hover', // hover
17498     
17499     delay : 0,
17500     
17501     over: 'parent',
17502     
17503     can_build_overlaid : false,
17504     
17505     getChildContainer : function()
17506     {
17507         return this.el.select('.popover-content',true).first();
17508     },
17509     
17510     getAutoCreate : function(){
17511          
17512         var cfg = {
17513            cls : 'popover roo-dynamic',
17514            style: 'display:block',
17515            cn : [
17516                 {
17517                     cls : 'arrow'
17518                 },
17519                 {
17520                     cls : 'popover-inner',
17521                     cn : [
17522                         {
17523                             tag: 'h3',
17524                             cls: 'popover-title',
17525                             html : this.title
17526                         },
17527                         {
17528                             cls : 'popover-content',
17529                             html : this.html
17530                         }
17531                     ]
17532                     
17533                 }
17534            ]
17535         };
17536         
17537         return cfg;
17538     },
17539     setTitle: function(str)
17540     {
17541         this.title = str;
17542         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17543     },
17544     setContent: function(str)
17545     {
17546         this.html = str;
17547         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17548     },
17549     // as it get's added to the bottom of the page.
17550     onRender : function(ct, position)
17551     {
17552         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17553         if(!this.el){
17554             var cfg = Roo.apply({},  this.getAutoCreate());
17555             cfg.id = Roo.id();
17556             
17557             if (this.cls) {
17558                 cfg.cls += ' ' + this.cls;
17559             }
17560             if (this.style) {
17561                 cfg.style = this.style;
17562             }
17563             //Roo.log("adding to ");
17564             this.el = Roo.get(document.body).createChild(cfg, position);
17565 //            Roo.log(this.el);
17566         }
17567         this.initEvents();
17568     },
17569     
17570     initEvents : function()
17571     {
17572         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17573         this.el.enableDisplayMode('block');
17574         this.el.hide();
17575         if (this.over === false) {
17576             return; 
17577         }
17578         if (this.triggers === false) {
17579             return;
17580         }
17581         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17582         var triggers = this.trigger ? this.trigger.split(' ') : [];
17583         Roo.each(triggers, function(trigger) {
17584         
17585             if (trigger == 'click') {
17586                 on_el.on('click', this.toggle, this);
17587             } else if (trigger != 'manual') {
17588                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17589                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17590       
17591                 on_el.on(eventIn  ,this.enter, this);
17592                 on_el.on(eventOut, this.leave, this);
17593             }
17594         }, this);
17595         
17596     },
17597     
17598     
17599     // private
17600     timeout : null,
17601     hoverState : null,
17602     
17603     toggle : function () {
17604         this.hoverState == 'in' ? this.leave() : this.enter();
17605     },
17606     
17607     enter : function () {
17608         
17609         clearTimeout(this.timeout);
17610     
17611         this.hoverState = 'in';
17612     
17613         if (!this.delay || !this.delay.show) {
17614             this.show();
17615             return;
17616         }
17617         var _t = this;
17618         this.timeout = setTimeout(function () {
17619             if (_t.hoverState == 'in') {
17620                 _t.show();
17621             }
17622         }, this.delay.show)
17623     },
17624     
17625     leave : function() {
17626         clearTimeout(this.timeout);
17627     
17628         this.hoverState = 'out';
17629     
17630         if (!this.delay || !this.delay.hide) {
17631             this.hide();
17632             return;
17633         }
17634         var _t = this;
17635         this.timeout = setTimeout(function () {
17636             if (_t.hoverState == 'out') {
17637                 _t.hide();
17638             }
17639         }, this.delay.hide)
17640     },
17641     
17642     show : function (on_el)
17643     {
17644         if (!on_el) {
17645             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17646         }
17647         
17648         // set content.
17649         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17650         if (this.html !== false) {
17651             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17652         }
17653         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17654         if (!this.title.length) {
17655             this.el.select('.popover-title',true).hide();
17656         }
17657         
17658         var placement = typeof this.placement == 'function' ?
17659             this.placement.call(this, this.el, on_el) :
17660             this.placement;
17661             
17662         var autoToken = /\s?auto?\s?/i;
17663         var autoPlace = autoToken.test(placement);
17664         if (autoPlace) {
17665             placement = placement.replace(autoToken, '') || 'top';
17666         }
17667         
17668         //this.el.detach()
17669         //this.el.setXY([0,0]);
17670         this.el.show();
17671         this.el.dom.style.display='block';
17672         this.el.addClass(placement);
17673         
17674         //this.el.appendTo(on_el);
17675         
17676         var p = this.getPosition();
17677         var box = this.el.getBox();
17678         
17679         if (autoPlace) {
17680             // fixme..
17681         }
17682         var align = Roo.bootstrap.Popover.alignment[placement];
17683         
17684 //        Roo.log(align);
17685         this.el.alignTo(on_el, align[0],align[1]);
17686         //var arrow = this.el.select('.arrow',true).first();
17687         //arrow.set(align[2], 
17688         
17689         this.el.addClass('in');
17690         
17691         
17692         if (this.el.hasClass('fade')) {
17693             // fade it?
17694         }
17695         
17696         this.hoverState = 'in';
17697         
17698         this.fireEvent('show', this);
17699         
17700     },
17701     hide : function()
17702     {
17703         this.el.setXY([0,0]);
17704         this.el.removeClass('in');
17705         this.el.hide();
17706         this.hoverState = null;
17707         
17708         this.fireEvent('hide', this);
17709     }
17710     
17711 });
17712
17713 Roo.bootstrap.Popover.alignment = {
17714     'left' : ['r-l', [-10,0], 'right'],
17715     'right' : ['l-r', [10,0], 'left'],
17716     'bottom' : ['t-b', [0,10], 'top'],
17717     'top' : [ 'b-t', [0,-10], 'bottom']
17718 };
17719
17720  /*
17721  * - LGPL
17722  *
17723  * Progress
17724  * 
17725  */
17726
17727 /**
17728  * @class Roo.bootstrap.Progress
17729  * @extends Roo.bootstrap.Component
17730  * Bootstrap Progress class
17731  * @cfg {Boolean} striped striped of the progress bar
17732  * @cfg {Boolean} active animated of the progress bar
17733  * 
17734  * 
17735  * @constructor
17736  * Create a new Progress
17737  * @param {Object} config The config object
17738  */
17739
17740 Roo.bootstrap.Progress = function(config){
17741     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17742 };
17743
17744 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17745     
17746     striped : false,
17747     active: false,
17748     
17749     getAutoCreate : function(){
17750         var cfg = {
17751             tag: 'div',
17752             cls: 'progress'
17753         };
17754         
17755         
17756         if(this.striped){
17757             cfg.cls += ' progress-striped';
17758         }
17759       
17760         if(this.active){
17761             cfg.cls += ' active';
17762         }
17763         
17764         
17765         return cfg;
17766     }
17767    
17768 });
17769
17770  
17771
17772  /*
17773  * - LGPL
17774  *
17775  * ProgressBar
17776  * 
17777  */
17778
17779 /**
17780  * @class Roo.bootstrap.ProgressBar
17781  * @extends Roo.bootstrap.Component
17782  * Bootstrap ProgressBar class
17783  * @cfg {Number} aria_valuenow aria-value now
17784  * @cfg {Number} aria_valuemin aria-value min
17785  * @cfg {Number} aria_valuemax aria-value max
17786  * @cfg {String} label label for the progress bar
17787  * @cfg {String} panel (success | info | warning | danger )
17788  * @cfg {String} role role of the progress bar
17789  * @cfg {String} sr_only text
17790  * 
17791  * 
17792  * @constructor
17793  * Create a new ProgressBar
17794  * @param {Object} config The config object
17795  */
17796
17797 Roo.bootstrap.ProgressBar = function(config){
17798     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17799 };
17800
17801 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17802     
17803     aria_valuenow : 0,
17804     aria_valuemin : 0,
17805     aria_valuemax : 100,
17806     label : false,
17807     panel : false,
17808     role : false,
17809     sr_only: false,
17810     
17811     getAutoCreate : function()
17812     {
17813         
17814         var cfg = {
17815             tag: 'div',
17816             cls: 'progress-bar',
17817             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17818         };
17819         
17820         if(this.sr_only){
17821             cfg.cn = {
17822                 tag: 'span',
17823                 cls: 'sr-only',
17824                 html: this.sr_only
17825             }
17826         }
17827         
17828         if(this.role){
17829             cfg.role = this.role;
17830         }
17831         
17832         if(this.aria_valuenow){
17833             cfg['aria-valuenow'] = this.aria_valuenow;
17834         }
17835         
17836         if(this.aria_valuemin){
17837             cfg['aria-valuemin'] = this.aria_valuemin;
17838         }
17839         
17840         if(this.aria_valuemax){
17841             cfg['aria-valuemax'] = this.aria_valuemax;
17842         }
17843         
17844         if(this.label && !this.sr_only){
17845             cfg.html = this.label;
17846         }
17847         
17848         if(this.panel){
17849             cfg.cls += ' progress-bar-' + this.panel;
17850         }
17851         
17852         return cfg;
17853     },
17854     
17855     update : function(aria_valuenow)
17856     {
17857         this.aria_valuenow = aria_valuenow;
17858         
17859         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17860     }
17861    
17862 });
17863
17864  
17865
17866  /*
17867  * - LGPL
17868  *
17869  * column
17870  * 
17871  */
17872
17873 /**
17874  * @class Roo.bootstrap.TabGroup
17875  * @extends Roo.bootstrap.Column
17876  * Bootstrap Column class
17877  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17878  * @cfg {Boolean} carousel true to make the group behave like a carousel
17879  * @cfg {Boolean} bullets show bullets for the panels
17880  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17881  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17882  * @cfg {Boolean} showarrow (true|false) show arrow default true
17883  * 
17884  * @constructor
17885  * Create a new TabGroup
17886  * @param {Object} config The config object
17887  */
17888
17889 Roo.bootstrap.TabGroup = function(config){
17890     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17891     if (!this.navId) {
17892         this.navId = Roo.id();
17893     }
17894     this.tabs = [];
17895     Roo.bootstrap.TabGroup.register(this);
17896     
17897 };
17898
17899 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17900     
17901     carousel : false,
17902     transition : false,
17903     bullets : 0,
17904     timer : 0,
17905     autoslide : false,
17906     slideFn : false,
17907     slideOnTouch : false,
17908     showarrow : true,
17909     
17910     getAutoCreate : function()
17911     {
17912         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17913         
17914         cfg.cls += ' tab-content';
17915         
17916         if (this.carousel) {
17917             cfg.cls += ' carousel slide';
17918             
17919             cfg.cn = [{
17920                cls : 'carousel-inner',
17921                cn : []
17922             }];
17923         
17924             if(this.bullets  && !Roo.isTouch){
17925                 
17926                 var bullets = {
17927                     cls : 'carousel-bullets',
17928                     cn : []
17929                 };
17930                
17931                 if(this.bullets_cls){
17932                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17933                 }
17934                 
17935                 bullets.cn.push({
17936                     cls : 'clear'
17937                 });
17938                 
17939                 cfg.cn[0].cn.push(bullets);
17940             }
17941             
17942             if(this.showarrow){
17943                 cfg.cn[0].cn.push({
17944                     tag : 'div',
17945                     class : 'carousel-arrow',
17946                     cn : [
17947                         {
17948                             tag : 'div',
17949                             class : 'carousel-prev',
17950                             cn : [
17951                                 {
17952                                     tag : 'i',
17953                                     class : 'fa fa-chevron-left'
17954                                 }
17955                             ]
17956                         },
17957                         {
17958                             tag : 'div',
17959                             class : 'carousel-next',
17960                             cn : [
17961                                 {
17962                                     tag : 'i',
17963                                     class : 'fa fa-chevron-right'
17964                                 }
17965                             ]
17966                         }
17967                     ]
17968                 });
17969             }
17970             
17971         }
17972         
17973         return cfg;
17974     },
17975     
17976     initEvents:  function()
17977     {
17978 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17979 //            this.el.on("touchstart", this.onTouchStart, this);
17980 //        }
17981         
17982         if(this.autoslide){
17983             var _this = this;
17984             
17985             this.slideFn = window.setInterval(function() {
17986                 _this.showPanelNext();
17987             }, this.timer);
17988         }
17989         
17990         if(this.showarrow){
17991             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17992             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17993         }
17994         
17995         
17996     },
17997     
17998 //    onTouchStart : function(e, el, o)
17999 //    {
18000 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18001 //            return;
18002 //        }
18003 //        
18004 //        this.showPanelNext();
18005 //    },
18006     
18007     
18008     getChildContainer : function()
18009     {
18010         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18011     },
18012     
18013     /**
18014     * register a Navigation item
18015     * @param {Roo.bootstrap.NavItem} the navitem to add
18016     */
18017     register : function(item)
18018     {
18019         this.tabs.push( item);
18020         item.navId = this.navId; // not really needed..
18021         this.addBullet();
18022     
18023     },
18024     
18025     getActivePanel : function()
18026     {
18027         var r = false;
18028         Roo.each(this.tabs, function(t) {
18029             if (t.active) {
18030                 r = t;
18031                 return false;
18032             }
18033             return null;
18034         });
18035         return r;
18036         
18037     },
18038     getPanelByName : function(n)
18039     {
18040         var r = false;
18041         Roo.each(this.tabs, function(t) {
18042             if (t.tabId == n) {
18043                 r = t;
18044                 return false;
18045             }
18046             return null;
18047         });
18048         return r;
18049     },
18050     indexOfPanel : function(p)
18051     {
18052         var r = false;
18053         Roo.each(this.tabs, function(t,i) {
18054             if (t.tabId == p.tabId) {
18055                 r = i;
18056                 return false;
18057             }
18058             return null;
18059         });
18060         return r;
18061     },
18062     /**
18063      * show a specific panel
18064      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18065      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18066      */
18067     showPanel : function (pan)
18068     {
18069         if(this.transition || typeof(pan) == 'undefined'){
18070             Roo.log("waiting for the transitionend");
18071             return;
18072         }
18073         
18074         if (typeof(pan) == 'number') {
18075             pan = this.tabs[pan];
18076         }
18077         
18078         if (typeof(pan) == 'string') {
18079             pan = this.getPanelByName(pan);
18080         }
18081         
18082         var cur = this.getActivePanel();
18083         
18084         if(!pan || !cur){
18085             Roo.log('pan or acitve pan is undefined');
18086             return false;
18087         }
18088         
18089         if (pan.tabId == this.getActivePanel().tabId) {
18090             return true;
18091         }
18092         
18093         if (false === cur.fireEvent('beforedeactivate')) {
18094             return false;
18095         }
18096         
18097         if(this.bullets > 0 && !Roo.isTouch){
18098             this.setActiveBullet(this.indexOfPanel(pan));
18099         }
18100         
18101         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18102             
18103             this.transition = true;
18104             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18105             var lr = dir == 'next' ? 'left' : 'right';
18106             pan.el.addClass(dir); // or prev
18107             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18108             cur.el.addClass(lr); // or right
18109             pan.el.addClass(lr);
18110             
18111             var _this = this;
18112             cur.el.on('transitionend', function() {
18113                 Roo.log("trans end?");
18114                 
18115                 pan.el.removeClass([lr,dir]);
18116                 pan.setActive(true);
18117                 
18118                 cur.el.removeClass([lr]);
18119                 cur.setActive(false);
18120                 
18121                 _this.transition = false;
18122                 
18123             }, this, { single:  true } );
18124             
18125             return true;
18126         }
18127         
18128         cur.setActive(false);
18129         pan.setActive(true);
18130         
18131         return true;
18132         
18133     },
18134     showPanelNext : function()
18135     {
18136         var i = this.indexOfPanel(this.getActivePanel());
18137         
18138         if (i >= this.tabs.length - 1 && !this.autoslide) {
18139             return;
18140         }
18141         
18142         if (i >= this.tabs.length - 1 && this.autoslide) {
18143             i = -1;
18144         }
18145         
18146         this.showPanel(this.tabs[i+1]);
18147     },
18148     
18149     showPanelPrev : function()
18150     {
18151         var i = this.indexOfPanel(this.getActivePanel());
18152         
18153         if (i  < 1 && !this.autoslide) {
18154             return;
18155         }
18156         
18157         if (i < 1 && this.autoslide) {
18158             i = this.tabs.length;
18159         }
18160         
18161         this.showPanel(this.tabs[i-1]);
18162     },
18163     
18164     
18165     addBullet: function()
18166     {
18167         if(!this.bullets || Roo.isTouch){
18168             return;
18169         }
18170         var ctr = this.el.select('.carousel-bullets',true).first();
18171         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18172         var bullet = ctr.createChild({
18173             cls : 'bullet bullet-' + i
18174         },ctr.dom.lastChild);
18175         
18176         
18177         var _this = this;
18178         
18179         bullet.on('click', (function(e, el, o, ii, t){
18180
18181             e.preventDefault();
18182
18183             this.showPanel(ii);
18184
18185             if(this.autoslide && this.slideFn){
18186                 clearInterval(this.slideFn);
18187                 this.slideFn = window.setInterval(function() {
18188                     _this.showPanelNext();
18189                 }, this.timer);
18190             }
18191
18192         }).createDelegate(this, [i, bullet], true));
18193                 
18194         
18195     },
18196      
18197     setActiveBullet : function(i)
18198     {
18199         if(Roo.isTouch){
18200             return;
18201         }
18202         
18203         Roo.each(this.el.select('.bullet', true).elements, function(el){
18204             el.removeClass('selected');
18205         });
18206
18207         var bullet = this.el.select('.bullet-' + i, true).first();
18208         
18209         if(!bullet){
18210             return;
18211         }
18212         
18213         bullet.addClass('selected');
18214     }
18215     
18216     
18217   
18218 });
18219
18220  
18221
18222  
18223  
18224 Roo.apply(Roo.bootstrap.TabGroup, {
18225     
18226     groups: {},
18227      /**
18228     * register a Navigation Group
18229     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18230     */
18231     register : function(navgrp)
18232     {
18233         this.groups[navgrp.navId] = navgrp;
18234         
18235     },
18236     /**
18237     * fetch a Navigation Group based on the navigation ID
18238     * if one does not exist , it will get created.
18239     * @param {string} the navgroup to add
18240     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18241     */
18242     get: function(navId) {
18243         if (typeof(this.groups[navId]) == 'undefined') {
18244             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18245         }
18246         return this.groups[navId] ;
18247     }
18248     
18249     
18250     
18251 });
18252
18253  /*
18254  * - LGPL
18255  *
18256  * TabPanel
18257  * 
18258  */
18259
18260 /**
18261  * @class Roo.bootstrap.TabPanel
18262  * @extends Roo.bootstrap.Component
18263  * Bootstrap TabPanel class
18264  * @cfg {Boolean} active panel active
18265  * @cfg {String} html panel content
18266  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18267  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18268  * @cfg {String} href click to link..
18269  * 
18270  * 
18271  * @constructor
18272  * Create a new TabPanel
18273  * @param {Object} config The config object
18274  */
18275
18276 Roo.bootstrap.TabPanel = function(config){
18277     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18278     this.addEvents({
18279         /**
18280              * @event changed
18281              * Fires when the active status changes
18282              * @param {Roo.bootstrap.TabPanel} this
18283              * @param {Boolean} state the new state
18284             
18285          */
18286         'changed': true,
18287         /**
18288              * @event beforedeactivate
18289              * Fires before a tab is de-activated - can be used to do validation on a form.
18290              * @param {Roo.bootstrap.TabPanel} this
18291              * @return {Boolean} false if there is an error
18292             
18293          */
18294         'beforedeactivate': true
18295      });
18296     
18297     this.tabId = this.tabId || Roo.id();
18298   
18299 };
18300
18301 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18302     
18303     active: false,
18304     html: false,
18305     tabId: false,
18306     navId : false,
18307     href : '',
18308     
18309     getAutoCreate : function(){
18310         var cfg = {
18311             tag: 'div',
18312             // item is needed for carousel - not sure if it has any effect otherwise
18313             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18314             html: this.html || ''
18315         };
18316         
18317         if(this.active){
18318             cfg.cls += ' active';
18319         }
18320         
18321         if(this.tabId){
18322             cfg.tabId = this.tabId;
18323         }
18324         
18325         
18326         return cfg;
18327     },
18328     
18329     initEvents:  function()
18330     {
18331         var p = this.parent();
18332         
18333         this.navId = this.navId || p.navId;
18334         
18335         if (typeof(this.navId) != 'undefined') {
18336             // not really needed.. but just in case.. parent should be a NavGroup.
18337             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18338             
18339             tg.register(this);
18340             
18341             var i = tg.tabs.length - 1;
18342             
18343             if(this.active && tg.bullets > 0 && i < tg.bullets){
18344                 tg.setActiveBullet(i);
18345             }
18346         }
18347         
18348         this.el.on('click', this.onClick, this);
18349         
18350         if(Roo.isTouch){
18351             this.el.on("touchstart", this.onTouchStart, this);
18352             this.el.on("touchmove", this.onTouchMove, this);
18353             this.el.on("touchend", this.onTouchEnd, this);
18354         }
18355         
18356     },
18357     
18358     onRender : function(ct, position)
18359     {
18360         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18361     },
18362     
18363     setActive : function(state)
18364     {
18365         Roo.log("panel - set active " + this.tabId + "=" + state);
18366         
18367         this.active = state;
18368         if (!state) {
18369             this.el.removeClass('active');
18370             
18371         } else  if (!this.el.hasClass('active')) {
18372             this.el.addClass('active');
18373         }
18374         
18375         this.fireEvent('changed', this, state);
18376     },
18377     
18378     onClick : function(e)
18379     {
18380         e.preventDefault();
18381         
18382         if(!this.href.length){
18383             return;
18384         }
18385         
18386         window.location.href = this.href;
18387     },
18388     
18389     startX : 0,
18390     startY : 0,
18391     endX : 0,
18392     endY : 0,
18393     swiping : false,
18394     
18395     onTouchStart : function(e)
18396     {
18397         this.swiping = false;
18398         
18399         this.startX = e.browserEvent.touches[0].clientX;
18400         this.startY = e.browserEvent.touches[0].clientY;
18401     },
18402     
18403     onTouchMove : function(e)
18404     {
18405         this.swiping = true;
18406         
18407         this.endX = e.browserEvent.touches[0].clientX;
18408         this.endY = e.browserEvent.touches[0].clientY;
18409     },
18410     
18411     onTouchEnd : function(e)
18412     {
18413         if(!this.swiping){
18414             this.onClick(e);
18415             return;
18416         }
18417         
18418         var tabGroup = this.parent();
18419         
18420         if(this.endX > this.startX){ // swiping right
18421             tabGroup.showPanelPrev();
18422             return;
18423         }
18424         
18425         if(this.startX > this.endX){ // swiping left
18426             tabGroup.showPanelNext();
18427             return;
18428         }
18429     }
18430     
18431     
18432 });
18433  
18434
18435  
18436
18437  /*
18438  * - LGPL
18439  *
18440  * DateField
18441  * 
18442  */
18443
18444 /**
18445  * @class Roo.bootstrap.DateField
18446  * @extends Roo.bootstrap.Input
18447  * Bootstrap DateField class
18448  * @cfg {Number} weekStart default 0
18449  * @cfg {String} viewMode default empty, (months|years)
18450  * @cfg {String} minViewMode default empty, (months|years)
18451  * @cfg {Number} startDate default -Infinity
18452  * @cfg {Number} endDate default Infinity
18453  * @cfg {Boolean} todayHighlight default false
18454  * @cfg {Boolean} todayBtn default false
18455  * @cfg {Boolean} calendarWeeks default false
18456  * @cfg {Object} daysOfWeekDisabled default empty
18457  * @cfg {Boolean} singleMode default false (true | false)
18458  * 
18459  * @cfg {Boolean} keyboardNavigation default true
18460  * @cfg {String} language default en
18461  * 
18462  * @constructor
18463  * Create a new DateField
18464  * @param {Object} config The config object
18465  */
18466
18467 Roo.bootstrap.DateField = function(config){
18468     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18469      this.addEvents({
18470             /**
18471              * @event show
18472              * Fires when this field show.
18473              * @param {Roo.bootstrap.DateField} this
18474              * @param {Mixed} date The date value
18475              */
18476             show : true,
18477             /**
18478              * @event show
18479              * Fires when this field hide.
18480              * @param {Roo.bootstrap.DateField} this
18481              * @param {Mixed} date The date value
18482              */
18483             hide : true,
18484             /**
18485              * @event select
18486              * Fires when select a date.
18487              * @param {Roo.bootstrap.DateField} this
18488              * @param {Mixed} date The date value
18489              */
18490             select : true,
18491             /**
18492              * @event beforeselect
18493              * Fires when before select a date.
18494              * @param {Roo.bootstrap.DateField} this
18495              * @param {Mixed} date The date value
18496              */
18497             beforeselect : true
18498         });
18499 };
18500
18501 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18502     
18503     /**
18504      * @cfg {String} format
18505      * The default date format string which can be overriden for localization support.  The format must be
18506      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18507      */
18508     format : "m/d/y",
18509     /**
18510      * @cfg {String} altFormats
18511      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18512      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18513      */
18514     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18515     
18516     weekStart : 0,
18517     
18518     viewMode : '',
18519     
18520     minViewMode : '',
18521     
18522     todayHighlight : false,
18523     
18524     todayBtn: false,
18525     
18526     language: 'en',
18527     
18528     keyboardNavigation: true,
18529     
18530     calendarWeeks: false,
18531     
18532     startDate: -Infinity,
18533     
18534     endDate: Infinity,
18535     
18536     daysOfWeekDisabled: [],
18537     
18538     _events: [],
18539     
18540     singleMode : false,
18541     
18542     UTCDate: function()
18543     {
18544         return new Date(Date.UTC.apply(Date, arguments));
18545     },
18546     
18547     UTCToday: function()
18548     {
18549         var today = new Date();
18550         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18551     },
18552     
18553     getDate: function() {
18554             var d = this.getUTCDate();
18555             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18556     },
18557     
18558     getUTCDate: function() {
18559             return this.date;
18560     },
18561     
18562     setDate: function(d) {
18563             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18564     },
18565     
18566     setUTCDate: function(d) {
18567             this.date = d;
18568             this.setValue(this.formatDate(this.date));
18569     },
18570         
18571     onRender: function(ct, position)
18572     {
18573         
18574         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18575         
18576         this.language = this.language || 'en';
18577         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18578         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18579         
18580         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18581         this.format = this.format || 'm/d/y';
18582         this.isInline = false;
18583         this.isInput = true;
18584         this.component = this.el.select('.add-on', true).first() || false;
18585         this.component = (this.component && this.component.length === 0) ? false : this.component;
18586         this.hasInput = this.component && this.inputEl().length;
18587         
18588         if (typeof(this.minViewMode === 'string')) {
18589             switch (this.minViewMode) {
18590                 case 'months':
18591                     this.minViewMode = 1;
18592                     break;
18593                 case 'years':
18594                     this.minViewMode = 2;
18595                     break;
18596                 default:
18597                     this.minViewMode = 0;
18598                     break;
18599             }
18600         }
18601         
18602         if (typeof(this.viewMode === 'string')) {
18603             switch (this.viewMode) {
18604                 case 'months':
18605                     this.viewMode = 1;
18606                     break;
18607                 case 'years':
18608                     this.viewMode = 2;
18609                     break;
18610                 default:
18611                     this.viewMode = 0;
18612                     break;
18613             }
18614         }
18615                 
18616         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18617         
18618 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18619         
18620         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18621         
18622         this.picker().on('mousedown', this.onMousedown, this);
18623         this.picker().on('click', this.onClick, this);
18624         
18625         this.picker().addClass('datepicker-dropdown');
18626         
18627         this.startViewMode = this.viewMode;
18628         
18629         if(this.singleMode){
18630             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18631                 v.setVisibilityMode(Roo.Element.DISPLAY);
18632                 v.hide();
18633             });
18634             
18635             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18636                 v.setStyle('width', '189px');
18637             });
18638         }
18639         
18640         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18641             if(!this.calendarWeeks){
18642                 v.remove();
18643                 return;
18644             }
18645             
18646             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18647             v.attr('colspan', function(i, val){
18648                 return parseInt(val) + 1;
18649             });
18650         });
18651                         
18652         
18653         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18654         
18655         this.setStartDate(this.startDate);
18656         this.setEndDate(this.endDate);
18657         
18658         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18659         
18660         this.fillDow();
18661         this.fillMonths();
18662         this.update();
18663         this.showMode();
18664         
18665         if(this.isInline) {
18666             this.showPopup();
18667         }
18668     },
18669     
18670     picker : function()
18671     {
18672         return this.pickerEl;
18673 //        return this.el.select('.datepicker', true).first();
18674     },
18675     
18676     fillDow: function()
18677     {
18678         var dowCnt = this.weekStart;
18679         
18680         var dow = {
18681             tag: 'tr',
18682             cn: [
18683                 
18684             ]
18685         };
18686         
18687         if(this.calendarWeeks){
18688             dow.cn.push({
18689                 tag: 'th',
18690                 cls: 'cw',
18691                 html: '&nbsp;'
18692             })
18693         }
18694         
18695         while (dowCnt < this.weekStart + 7) {
18696             dow.cn.push({
18697                 tag: 'th',
18698                 cls: 'dow',
18699                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18700             });
18701         }
18702         
18703         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18704     },
18705     
18706     fillMonths: function()
18707     {    
18708         var i = 0;
18709         var months = this.picker().select('>.datepicker-months td', true).first();
18710         
18711         months.dom.innerHTML = '';
18712         
18713         while (i < 12) {
18714             var month = {
18715                 tag: 'span',
18716                 cls: 'month',
18717                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18718             };
18719             
18720             months.createChild(month);
18721         }
18722         
18723     },
18724     
18725     update: function()
18726     {
18727         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;
18728         
18729         if (this.date < this.startDate) {
18730             this.viewDate = new Date(this.startDate);
18731         } else if (this.date > this.endDate) {
18732             this.viewDate = new Date(this.endDate);
18733         } else {
18734             this.viewDate = new Date(this.date);
18735         }
18736         
18737         this.fill();
18738     },
18739     
18740     fill: function() 
18741     {
18742         var d = new Date(this.viewDate),
18743                 year = d.getUTCFullYear(),
18744                 month = d.getUTCMonth(),
18745                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18746                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18747                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18748                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18749                 currentDate = this.date && this.date.valueOf(),
18750                 today = this.UTCToday();
18751         
18752         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18753         
18754 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18755         
18756 //        this.picker.select('>tfoot th.today').
18757 //                                              .text(dates[this.language].today)
18758 //                                              .toggle(this.todayBtn !== false);
18759     
18760         this.updateNavArrows();
18761         this.fillMonths();
18762                                                 
18763         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18764         
18765         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18766          
18767         prevMonth.setUTCDate(day);
18768         
18769         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18770         
18771         var nextMonth = new Date(prevMonth);
18772         
18773         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18774         
18775         nextMonth = nextMonth.valueOf();
18776         
18777         var fillMonths = false;
18778         
18779         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18780         
18781         while(prevMonth.valueOf() <= nextMonth) {
18782             var clsName = '';
18783             
18784             if (prevMonth.getUTCDay() === this.weekStart) {
18785                 if(fillMonths){
18786                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18787                 }
18788                     
18789                 fillMonths = {
18790                     tag: 'tr',
18791                     cn: []
18792                 };
18793                 
18794                 if(this.calendarWeeks){
18795                     // ISO 8601: First week contains first thursday.
18796                     // ISO also states week starts on Monday, but we can be more abstract here.
18797                     var
18798                     // Start of current week: based on weekstart/current date
18799                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18800                     // Thursday of this week
18801                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18802                     // First Thursday of year, year from thursday
18803                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18804                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18805                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18806                     
18807                     fillMonths.cn.push({
18808                         tag: 'td',
18809                         cls: 'cw',
18810                         html: calWeek
18811                     });
18812                 }
18813             }
18814             
18815             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18816                 clsName += ' old';
18817             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18818                 clsName += ' new';
18819             }
18820             if (this.todayHighlight &&
18821                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18822                 prevMonth.getUTCMonth() == today.getMonth() &&
18823                 prevMonth.getUTCDate() == today.getDate()) {
18824                 clsName += ' today';
18825             }
18826             
18827             if (currentDate && prevMonth.valueOf() === currentDate) {
18828                 clsName += ' active';
18829             }
18830             
18831             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18832                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18833                     clsName += ' disabled';
18834             }
18835             
18836             fillMonths.cn.push({
18837                 tag: 'td',
18838                 cls: 'day ' + clsName,
18839                 html: prevMonth.getDate()
18840             });
18841             
18842             prevMonth.setDate(prevMonth.getDate()+1);
18843         }
18844           
18845         var currentYear = this.date && this.date.getUTCFullYear();
18846         var currentMonth = this.date && this.date.getUTCMonth();
18847         
18848         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18849         
18850         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18851             v.removeClass('active');
18852             
18853             if(currentYear === year && k === currentMonth){
18854                 v.addClass('active');
18855             }
18856             
18857             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18858                 v.addClass('disabled');
18859             }
18860             
18861         });
18862         
18863         
18864         year = parseInt(year/10, 10) * 10;
18865         
18866         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18867         
18868         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18869         
18870         year -= 1;
18871         for (var i = -1; i < 11; i++) {
18872             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18873                 tag: 'span',
18874                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18875                 html: year
18876             });
18877             
18878             year += 1;
18879         }
18880     },
18881     
18882     showMode: function(dir) 
18883     {
18884         if (dir) {
18885             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18886         }
18887         
18888         Roo.each(this.picker().select('>div',true).elements, function(v){
18889             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18890             v.hide();
18891         });
18892         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18893     },
18894     
18895     place: function()
18896     {
18897         if(this.isInline) {
18898             return;
18899         }
18900         
18901         this.picker().removeClass(['bottom', 'top']);
18902         
18903         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18904             /*
18905              * place to the top of element!
18906              *
18907              */
18908             
18909             this.picker().addClass('top');
18910             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18911             
18912             return;
18913         }
18914         
18915         this.picker().addClass('bottom');
18916         
18917         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18918     },
18919     
18920     parseDate : function(value)
18921     {
18922         if(!value || value instanceof Date){
18923             return value;
18924         }
18925         var v = Date.parseDate(value, this.format);
18926         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18927             v = Date.parseDate(value, 'Y-m-d');
18928         }
18929         if(!v && this.altFormats){
18930             if(!this.altFormatsArray){
18931                 this.altFormatsArray = this.altFormats.split("|");
18932             }
18933             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18934                 v = Date.parseDate(value, this.altFormatsArray[i]);
18935             }
18936         }
18937         return v;
18938     },
18939     
18940     formatDate : function(date, fmt)
18941     {   
18942         return (!date || !(date instanceof Date)) ?
18943         date : date.dateFormat(fmt || this.format);
18944     },
18945     
18946     onFocus : function()
18947     {
18948         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18949         this.showPopup();
18950     },
18951     
18952     onBlur : function()
18953     {
18954         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18955         
18956         var d = this.inputEl().getValue();
18957         
18958         this.setValue(d);
18959                 
18960         this.hidePopup();
18961     },
18962     
18963     showPopup : function()
18964     {
18965         this.picker().show();
18966         this.update();
18967         this.place();
18968         
18969         this.fireEvent('showpopup', this, this.date);
18970     },
18971     
18972     hidePopup : function()
18973     {
18974         if(this.isInline) {
18975             return;
18976         }
18977         this.picker().hide();
18978         this.viewMode = this.startViewMode;
18979         this.showMode();
18980         
18981         this.fireEvent('hidepopup', this, this.date);
18982         
18983     },
18984     
18985     onMousedown: function(e)
18986     {
18987         e.stopPropagation();
18988         e.preventDefault();
18989     },
18990     
18991     keyup: function(e)
18992     {
18993         Roo.bootstrap.DateField.superclass.keyup.call(this);
18994         this.update();
18995     },
18996
18997     setValue: function(v)
18998     {
18999         if(this.fireEvent('beforeselect', this, v) !== false){
19000             var d = new Date(this.parseDate(v) ).clearTime();
19001         
19002             if(isNaN(d.getTime())){
19003                 this.date = this.viewDate = '';
19004                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19005                 return;
19006             }
19007
19008             v = this.formatDate(d);
19009
19010             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19011
19012             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19013
19014             this.update();
19015
19016             this.fireEvent('select', this, this.date);
19017         }
19018     },
19019     
19020     getValue: function()
19021     {
19022         return this.formatDate(this.date);
19023     },
19024     
19025     fireKey: function(e)
19026     {
19027         if (!this.picker().isVisible()){
19028             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19029                 this.showPopup();
19030             }
19031             return;
19032         }
19033         
19034         var dateChanged = false,
19035         dir, day, month,
19036         newDate, newViewDate;
19037         
19038         switch(e.keyCode){
19039             case 27: // escape
19040                 this.hidePopup();
19041                 e.preventDefault();
19042                 break;
19043             case 37: // left
19044             case 39: // right
19045                 if (!this.keyboardNavigation) {
19046                     break;
19047                 }
19048                 dir = e.keyCode == 37 ? -1 : 1;
19049                 
19050                 if (e.ctrlKey){
19051                     newDate = this.moveYear(this.date, dir);
19052                     newViewDate = this.moveYear(this.viewDate, dir);
19053                 } else if (e.shiftKey){
19054                     newDate = this.moveMonth(this.date, dir);
19055                     newViewDate = this.moveMonth(this.viewDate, dir);
19056                 } else {
19057                     newDate = new Date(this.date);
19058                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19059                     newViewDate = new Date(this.viewDate);
19060                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19061                 }
19062                 if (this.dateWithinRange(newDate)){
19063                     this.date = newDate;
19064                     this.viewDate = newViewDate;
19065                     this.setValue(this.formatDate(this.date));
19066 //                    this.update();
19067                     e.preventDefault();
19068                     dateChanged = true;
19069                 }
19070                 break;
19071             case 38: // up
19072             case 40: // down
19073                 if (!this.keyboardNavigation) {
19074                     break;
19075                 }
19076                 dir = e.keyCode == 38 ? -1 : 1;
19077                 if (e.ctrlKey){
19078                     newDate = this.moveYear(this.date, dir);
19079                     newViewDate = this.moveYear(this.viewDate, dir);
19080                 } else if (e.shiftKey){
19081                     newDate = this.moveMonth(this.date, dir);
19082                     newViewDate = this.moveMonth(this.viewDate, dir);
19083                 } else {
19084                     newDate = new Date(this.date);
19085                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19086                     newViewDate = new Date(this.viewDate);
19087                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19088                 }
19089                 if (this.dateWithinRange(newDate)){
19090                     this.date = newDate;
19091                     this.viewDate = newViewDate;
19092                     this.setValue(this.formatDate(this.date));
19093 //                    this.update();
19094                     e.preventDefault();
19095                     dateChanged = true;
19096                 }
19097                 break;
19098             case 13: // enter
19099                 this.setValue(this.formatDate(this.date));
19100                 this.hidePopup();
19101                 e.preventDefault();
19102                 break;
19103             case 9: // tab
19104                 this.setValue(this.formatDate(this.date));
19105                 this.hidePopup();
19106                 break;
19107             case 16: // shift
19108             case 17: // ctrl
19109             case 18: // alt
19110                 break;
19111             default :
19112                 this.hide();
19113                 
19114         }
19115     },
19116     
19117     
19118     onClick: function(e) 
19119     {
19120         e.stopPropagation();
19121         e.preventDefault();
19122         
19123         var target = e.getTarget();
19124         
19125         if(target.nodeName.toLowerCase() === 'i'){
19126             target = Roo.get(target).dom.parentNode;
19127         }
19128         
19129         var nodeName = target.nodeName;
19130         var className = target.className;
19131         var html = target.innerHTML;
19132         //Roo.log(nodeName);
19133         
19134         switch(nodeName.toLowerCase()) {
19135             case 'th':
19136                 switch(className) {
19137                     case 'switch':
19138                         this.showMode(1);
19139                         break;
19140                     case 'prev':
19141                     case 'next':
19142                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19143                         switch(this.viewMode){
19144                                 case 0:
19145                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19146                                         break;
19147                                 case 1:
19148                                 case 2:
19149                                         this.viewDate = this.moveYear(this.viewDate, dir);
19150                                         break;
19151                         }
19152                         this.fill();
19153                         break;
19154                     case 'today':
19155                         var date = new Date();
19156                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19157 //                        this.fill()
19158                         this.setValue(this.formatDate(this.date));
19159                         
19160                         this.hidePopup();
19161                         break;
19162                 }
19163                 break;
19164             case 'span':
19165                 if (className.indexOf('disabled') < 0) {
19166                     this.viewDate.setUTCDate(1);
19167                     if (className.indexOf('month') > -1) {
19168                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19169                     } else {
19170                         var year = parseInt(html, 10) || 0;
19171                         this.viewDate.setUTCFullYear(year);
19172                         
19173                     }
19174                     
19175                     if(this.singleMode){
19176                         this.setValue(this.formatDate(this.viewDate));
19177                         this.hidePopup();
19178                         return;
19179                     }
19180                     
19181                     this.showMode(-1);
19182                     this.fill();
19183                 }
19184                 break;
19185                 
19186             case 'td':
19187                 //Roo.log(className);
19188                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19189                     var day = parseInt(html, 10) || 1;
19190                     var year = this.viewDate.getUTCFullYear(),
19191                         month = this.viewDate.getUTCMonth();
19192
19193                     if (className.indexOf('old') > -1) {
19194                         if(month === 0 ){
19195                             month = 11;
19196                             year -= 1;
19197                         }else{
19198                             month -= 1;
19199                         }
19200                     } else if (className.indexOf('new') > -1) {
19201                         if (month == 11) {
19202                             month = 0;
19203                             year += 1;
19204                         } else {
19205                             month += 1;
19206                         }
19207                     }
19208                     //Roo.log([year,month,day]);
19209                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19210                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19211 //                    this.fill();
19212                     //Roo.log(this.formatDate(this.date));
19213                     this.setValue(this.formatDate(this.date));
19214                     this.hidePopup();
19215                 }
19216                 break;
19217         }
19218     },
19219     
19220     setStartDate: function(startDate)
19221     {
19222         this.startDate = startDate || -Infinity;
19223         if (this.startDate !== -Infinity) {
19224             this.startDate = this.parseDate(this.startDate);
19225         }
19226         this.update();
19227         this.updateNavArrows();
19228     },
19229
19230     setEndDate: function(endDate)
19231     {
19232         this.endDate = endDate || Infinity;
19233         if (this.endDate !== Infinity) {
19234             this.endDate = this.parseDate(this.endDate);
19235         }
19236         this.update();
19237         this.updateNavArrows();
19238     },
19239     
19240     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19241     {
19242         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19243         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19244             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19245         }
19246         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19247             return parseInt(d, 10);
19248         });
19249         this.update();
19250         this.updateNavArrows();
19251     },
19252     
19253     updateNavArrows: function() 
19254     {
19255         if(this.singleMode){
19256             return;
19257         }
19258         
19259         var d = new Date(this.viewDate),
19260         year = d.getUTCFullYear(),
19261         month = d.getUTCMonth();
19262         
19263         Roo.each(this.picker().select('.prev', true).elements, function(v){
19264             v.show();
19265             switch (this.viewMode) {
19266                 case 0:
19267
19268                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19269                         v.hide();
19270                     }
19271                     break;
19272                 case 1:
19273                 case 2:
19274                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19275                         v.hide();
19276                     }
19277                     break;
19278             }
19279         });
19280         
19281         Roo.each(this.picker().select('.next', true).elements, function(v){
19282             v.show();
19283             switch (this.viewMode) {
19284                 case 0:
19285
19286                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19287                         v.hide();
19288                     }
19289                     break;
19290                 case 1:
19291                 case 2:
19292                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19293                         v.hide();
19294                     }
19295                     break;
19296             }
19297         })
19298     },
19299     
19300     moveMonth: function(date, dir)
19301     {
19302         if (!dir) {
19303             return date;
19304         }
19305         var new_date = new Date(date.valueOf()),
19306         day = new_date.getUTCDate(),
19307         month = new_date.getUTCMonth(),
19308         mag = Math.abs(dir),
19309         new_month, test;
19310         dir = dir > 0 ? 1 : -1;
19311         if (mag == 1){
19312             test = dir == -1
19313             // If going back one month, make sure month is not current month
19314             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19315             ? function(){
19316                 return new_date.getUTCMonth() == month;
19317             }
19318             // If going forward one month, make sure month is as expected
19319             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19320             : function(){
19321                 return new_date.getUTCMonth() != new_month;
19322             };
19323             new_month = month + dir;
19324             new_date.setUTCMonth(new_month);
19325             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19326             if (new_month < 0 || new_month > 11) {
19327                 new_month = (new_month + 12) % 12;
19328             }
19329         } else {
19330             // For magnitudes >1, move one month at a time...
19331             for (var i=0; i<mag; i++) {
19332                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19333                 new_date = this.moveMonth(new_date, dir);
19334             }
19335             // ...then reset the day, keeping it in the new month
19336             new_month = new_date.getUTCMonth();
19337             new_date.setUTCDate(day);
19338             test = function(){
19339                 return new_month != new_date.getUTCMonth();
19340             };
19341         }
19342         // Common date-resetting loop -- if date is beyond end of month, make it
19343         // end of month
19344         while (test()){
19345             new_date.setUTCDate(--day);
19346             new_date.setUTCMonth(new_month);
19347         }
19348         return new_date;
19349     },
19350
19351     moveYear: function(date, dir)
19352     {
19353         return this.moveMonth(date, dir*12);
19354     },
19355
19356     dateWithinRange: function(date)
19357     {
19358         return date >= this.startDate && date <= this.endDate;
19359     },
19360
19361     
19362     remove: function() 
19363     {
19364         this.picker().remove();
19365     },
19366     
19367     validateValue : function(value)
19368     {
19369         if(this.getVisibilityEl().hasClass('hidden')){
19370             return true;
19371         }
19372         
19373         if(value.length < 1)  {
19374             if(this.allowBlank){
19375                 return true;
19376             }
19377             return false;
19378         }
19379         
19380         if(value.length < this.minLength){
19381             return false;
19382         }
19383         if(value.length > this.maxLength){
19384             return false;
19385         }
19386         if(this.vtype){
19387             var vt = Roo.form.VTypes;
19388             if(!vt[this.vtype](value, this)){
19389                 return false;
19390             }
19391         }
19392         if(typeof this.validator == "function"){
19393             var msg = this.validator(value);
19394             if(msg !== true){
19395                 return false;
19396             }
19397         }
19398         
19399         if(this.regex && !this.regex.test(value)){
19400             return false;
19401         }
19402         
19403         if(typeof(this.parseDate(value)) == 'undefined'){
19404             return false;
19405         }
19406         
19407         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19408             return false;
19409         }      
19410         
19411         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19412             return false;
19413         } 
19414         
19415         
19416         return true;
19417     },
19418     
19419     reset : function()
19420     {
19421         this.date = this.viewDate = '';
19422         
19423         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19424     }
19425    
19426 });
19427
19428 Roo.apply(Roo.bootstrap.DateField,  {
19429     
19430     head : {
19431         tag: 'thead',
19432         cn: [
19433         {
19434             tag: 'tr',
19435             cn: [
19436             {
19437                 tag: 'th',
19438                 cls: 'prev',
19439                 html: '<i class="fa fa-arrow-left"/>'
19440             },
19441             {
19442                 tag: 'th',
19443                 cls: 'switch',
19444                 colspan: '5'
19445             },
19446             {
19447                 tag: 'th',
19448                 cls: 'next',
19449                 html: '<i class="fa fa-arrow-right"/>'
19450             }
19451
19452             ]
19453         }
19454         ]
19455     },
19456     
19457     content : {
19458         tag: 'tbody',
19459         cn: [
19460         {
19461             tag: 'tr',
19462             cn: [
19463             {
19464                 tag: 'td',
19465                 colspan: '7'
19466             }
19467             ]
19468         }
19469         ]
19470     },
19471     
19472     footer : {
19473         tag: 'tfoot',
19474         cn: [
19475         {
19476             tag: 'tr',
19477             cn: [
19478             {
19479                 tag: 'th',
19480                 colspan: '7',
19481                 cls: 'today'
19482             }
19483                     
19484             ]
19485         }
19486         ]
19487     },
19488     
19489     dates:{
19490         en: {
19491             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19492             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19493             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19494             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19495             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19496             today: "Today"
19497         }
19498     },
19499     
19500     modes: [
19501     {
19502         clsName: 'days',
19503         navFnc: 'Month',
19504         navStep: 1
19505     },
19506     {
19507         clsName: 'months',
19508         navFnc: 'FullYear',
19509         navStep: 1
19510     },
19511     {
19512         clsName: 'years',
19513         navFnc: 'FullYear',
19514         navStep: 10
19515     }]
19516 });
19517
19518 Roo.apply(Roo.bootstrap.DateField,  {
19519   
19520     template : {
19521         tag: 'div',
19522         cls: 'datepicker dropdown-menu roo-dynamic',
19523         cn: [
19524         {
19525             tag: 'div',
19526             cls: 'datepicker-days',
19527             cn: [
19528             {
19529                 tag: 'table',
19530                 cls: 'table-condensed',
19531                 cn:[
19532                 Roo.bootstrap.DateField.head,
19533                 {
19534                     tag: 'tbody'
19535                 },
19536                 Roo.bootstrap.DateField.footer
19537                 ]
19538             }
19539             ]
19540         },
19541         {
19542             tag: 'div',
19543             cls: 'datepicker-months',
19544             cn: [
19545             {
19546                 tag: 'table',
19547                 cls: 'table-condensed',
19548                 cn:[
19549                 Roo.bootstrap.DateField.head,
19550                 Roo.bootstrap.DateField.content,
19551                 Roo.bootstrap.DateField.footer
19552                 ]
19553             }
19554             ]
19555         },
19556         {
19557             tag: 'div',
19558             cls: 'datepicker-years',
19559             cn: [
19560             {
19561                 tag: 'table',
19562                 cls: 'table-condensed',
19563                 cn:[
19564                 Roo.bootstrap.DateField.head,
19565                 Roo.bootstrap.DateField.content,
19566                 Roo.bootstrap.DateField.footer
19567                 ]
19568             }
19569             ]
19570         }
19571         ]
19572     }
19573 });
19574
19575  
19576
19577  /*
19578  * - LGPL
19579  *
19580  * TimeField
19581  * 
19582  */
19583
19584 /**
19585  * @class Roo.bootstrap.TimeField
19586  * @extends Roo.bootstrap.Input
19587  * Bootstrap DateField class
19588  * 
19589  * 
19590  * @constructor
19591  * Create a new TimeField
19592  * @param {Object} config The config object
19593  */
19594
19595 Roo.bootstrap.TimeField = function(config){
19596     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19597     this.addEvents({
19598             /**
19599              * @event show
19600              * Fires when this field show.
19601              * @param {Roo.bootstrap.DateField} thisthis
19602              * @param {Mixed} date The date value
19603              */
19604             show : true,
19605             /**
19606              * @event show
19607              * Fires when this field hide.
19608              * @param {Roo.bootstrap.DateField} this
19609              * @param {Mixed} date The date value
19610              */
19611             hide : true,
19612             /**
19613              * @event select
19614              * Fires when select a date.
19615              * @param {Roo.bootstrap.DateField} this
19616              * @param {Mixed} date The date value
19617              */
19618             select : true
19619         });
19620 };
19621
19622 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19623     
19624     /**
19625      * @cfg {String} format
19626      * The default time format string which can be overriden for localization support.  The format must be
19627      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19628      */
19629     format : "H:i",
19630        
19631     onRender: function(ct, position)
19632     {
19633         
19634         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19635                 
19636         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19637         
19638         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19639         
19640         this.pop = this.picker().select('>.datepicker-time',true).first();
19641         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19642         
19643         this.picker().on('mousedown', this.onMousedown, this);
19644         this.picker().on('click', this.onClick, this);
19645         
19646         this.picker().addClass('datepicker-dropdown');
19647     
19648         this.fillTime();
19649         this.update();
19650             
19651         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19652         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19653         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19654         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19655         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19656         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19657
19658     },
19659     
19660     fireKey: function(e){
19661         if (!this.picker().isVisible()){
19662             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19663                 this.show();
19664             }
19665             return;
19666         }
19667
19668         e.preventDefault();
19669         
19670         switch(e.keyCode){
19671             case 27: // escape
19672                 this.hide();
19673                 break;
19674             case 37: // left
19675             case 39: // right
19676                 this.onTogglePeriod();
19677                 break;
19678             case 38: // up
19679                 this.onIncrementMinutes();
19680                 break;
19681             case 40: // down
19682                 this.onDecrementMinutes();
19683                 break;
19684             case 13: // enter
19685             case 9: // tab
19686                 this.setTime();
19687                 break;
19688         }
19689     },
19690     
19691     onClick: function(e) {
19692         e.stopPropagation();
19693         e.preventDefault();
19694     },
19695     
19696     picker : function()
19697     {
19698         return this.el.select('.datepicker', true).first();
19699     },
19700     
19701     fillTime: function()
19702     {    
19703         var time = this.pop.select('tbody', true).first();
19704         
19705         time.dom.innerHTML = '';
19706         
19707         time.createChild({
19708             tag: 'tr',
19709             cn: [
19710                 {
19711                     tag: 'td',
19712                     cn: [
19713                         {
19714                             tag: 'a',
19715                             href: '#',
19716                             cls: 'btn',
19717                             cn: [
19718                                 {
19719                                     tag: 'span',
19720                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19721                                 }
19722                             ]
19723                         } 
19724                     ]
19725                 },
19726                 {
19727                     tag: 'td',
19728                     cls: 'separator'
19729                 },
19730                 {
19731                     tag: 'td',
19732                     cn: [
19733                         {
19734                             tag: 'a',
19735                             href: '#',
19736                             cls: 'btn',
19737                             cn: [
19738                                 {
19739                                     tag: 'span',
19740                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19741                                 }
19742                             ]
19743                         }
19744                     ]
19745                 },
19746                 {
19747                     tag: 'td',
19748                     cls: 'separator'
19749                 }
19750             ]
19751         });
19752         
19753         time.createChild({
19754             tag: 'tr',
19755             cn: [
19756                 {
19757                     tag: 'td',
19758                     cn: [
19759                         {
19760                             tag: 'span',
19761                             cls: 'timepicker-hour',
19762                             html: '00'
19763                         }  
19764                     ]
19765                 },
19766                 {
19767                     tag: 'td',
19768                     cls: 'separator',
19769                     html: ':'
19770                 },
19771                 {
19772                     tag: 'td',
19773                     cn: [
19774                         {
19775                             tag: 'span',
19776                             cls: 'timepicker-minute',
19777                             html: '00'
19778                         }  
19779                     ]
19780                 },
19781                 {
19782                     tag: 'td',
19783                     cls: 'separator'
19784                 },
19785                 {
19786                     tag: 'td',
19787                     cn: [
19788                         {
19789                             tag: 'button',
19790                             type: 'button',
19791                             cls: 'btn btn-primary period',
19792                             html: 'AM'
19793                             
19794                         }
19795                     ]
19796                 }
19797             ]
19798         });
19799         
19800         time.createChild({
19801             tag: 'tr',
19802             cn: [
19803                 {
19804                     tag: 'td',
19805                     cn: [
19806                         {
19807                             tag: 'a',
19808                             href: '#',
19809                             cls: 'btn',
19810                             cn: [
19811                                 {
19812                                     tag: 'span',
19813                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19814                                 }
19815                             ]
19816                         }
19817                     ]
19818                 },
19819                 {
19820                     tag: 'td',
19821                     cls: 'separator'
19822                 },
19823                 {
19824                     tag: 'td',
19825                     cn: [
19826                         {
19827                             tag: 'a',
19828                             href: '#',
19829                             cls: 'btn',
19830                             cn: [
19831                                 {
19832                                     tag: 'span',
19833                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19834                                 }
19835                             ]
19836                         }
19837                     ]
19838                 },
19839                 {
19840                     tag: 'td',
19841                     cls: 'separator'
19842                 }
19843             ]
19844         });
19845         
19846     },
19847     
19848     update: function()
19849     {
19850         
19851         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19852         
19853         this.fill();
19854     },
19855     
19856     fill: function() 
19857     {
19858         var hours = this.time.getHours();
19859         var minutes = this.time.getMinutes();
19860         var period = 'AM';
19861         
19862         if(hours > 11){
19863             period = 'PM';
19864         }
19865         
19866         if(hours == 0){
19867             hours = 12;
19868         }
19869         
19870         
19871         if(hours > 12){
19872             hours = hours - 12;
19873         }
19874         
19875         if(hours < 10){
19876             hours = '0' + hours;
19877         }
19878         
19879         if(minutes < 10){
19880             minutes = '0' + minutes;
19881         }
19882         
19883         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19884         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19885         this.pop.select('button', true).first().dom.innerHTML = period;
19886         
19887     },
19888     
19889     place: function()
19890     {   
19891         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19892         
19893         var cls = ['bottom'];
19894         
19895         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19896             cls.pop();
19897             cls.push('top');
19898         }
19899         
19900         cls.push('right');
19901         
19902         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19903             cls.pop();
19904             cls.push('left');
19905         }
19906         
19907         this.picker().addClass(cls.join('-'));
19908         
19909         var _this = this;
19910         
19911         Roo.each(cls, function(c){
19912             if(c == 'bottom'){
19913                 _this.picker().setTop(_this.inputEl().getHeight());
19914                 return;
19915             }
19916             if(c == 'top'){
19917                 _this.picker().setTop(0 - _this.picker().getHeight());
19918                 return;
19919             }
19920             
19921             if(c == 'left'){
19922                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19923                 return;
19924             }
19925             if(c == 'right'){
19926                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19927                 return;
19928             }
19929         });
19930         
19931     },
19932   
19933     onFocus : function()
19934     {
19935         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19936         this.show();
19937     },
19938     
19939     onBlur : function()
19940     {
19941         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19942         this.hide();
19943     },
19944     
19945     show : function()
19946     {
19947         this.picker().show();
19948         this.pop.show();
19949         this.update();
19950         this.place();
19951         
19952         this.fireEvent('show', this, this.date);
19953     },
19954     
19955     hide : function()
19956     {
19957         this.picker().hide();
19958         this.pop.hide();
19959         
19960         this.fireEvent('hide', this, this.date);
19961     },
19962     
19963     setTime : function()
19964     {
19965         this.hide();
19966         this.setValue(this.time.format(this.format));
19967         
19968         this.fireEvent('select', this, this.date);
19969         
19970         
19971     },
19972     
19973     onMousedown: function(e){
19974         e.stopPropagation();
19975         e.preventDefault();
19976     },
19977     
19978     onIncrementHours: function()
19979     {
19980         Roo.log('onIncrementHours');
19981         this.time = this.time.add(Date.HOUR, 1);
19982         this.update();
19983         
19984     },
19985     
19986     onDecrementHours: function()
19987     {
19988         Roo.log('onDecrementHours');
19989         this.time = this.time.add(Date.HOUR, -1);
19990         this.update();
19991     },
19992     
19993     onIncrementMinutes: function()
19994     {
19995         Roo.log('onIncrementMinutes');
19996         this.time = this.time.add(Date.MINUTE, 1);
19997         this.update();
19998     },
19999     
20000     onDecrementMinutes: function()
20001     {
20002         Roo.log('onDecrementMinutes');
20003         this.time = this.time.add(Date.MINUTE, -1);
20004         this.update();
20005     },
20006     
20007     onTogglePeriod: function()
20008     {
20009         Roo.log('onTogglePeriod');
20010         this.time = this.time.add(Date.HOUR, 12);
20011         this.update();
20012     }
20013     
20014    
20015 });
20016
20017 Roo.apply(Roo.bootstrap.TimeField,  {
20018     
20019     content : {
20020         tag: 'tbody',
20021         cn: [
20022             {
20023                 tag: 'tr',
20024                 cn: [
20025                 {
20026                     tag: 'td',
20027                     colspan: '7'
20028                 }
20029                 ]
20030             }
20031         ]
20032     },
20033     
20034     footer : {
20035         tag: 'tfoot',
20036         cn: [
20037             {
20038                 tag: 'tr',
20039                 cn: [
20040                 {
20041                     tag: 'th',
20042                     colspan: '7',
20043                     cls: '',
20044                     cn: [
20045                         {
20046                             tag: 'button',
20047                             cls: 'btn btn-info ok',
20048                             html: 'OK'
20049                         }
20050                     ]
20051                 }
20052
20053                 ]
20054             }
20055         ]
20056     }
20057 });
20058
20059 Roo.apply(Roo.bootstrap.TimeField,  {
20060   
20061     template : {
20062         tag: 'div',
20063         cls: 'datepicker dropdown-menu',
20064         cn: [
20065             {
20066                 tag: 'div',
20067                 cls: 'datepicker-time',
20068                 cn: [
20069                 {
20070                     tag: 'table',
20071                     cls: 'table-condensed',
20072                     cn:[
20073                     Roo.bootstrap.TimeField.content,
20074                     Roo.bootstrap.TimeField.footer
20075                     ]
20076                 }
20077                 ]
20078             }
20079         ]
20080     }
20081 });
20082
20083  
20084
20085  /*
20086  * - LGPL
20087  *
20088  * MonthField
20089  * 
20090  */
20091
20092 /**
20093  * @class Roo.bootstrap.MonthField
20094  * @extends Roo.bootstrap.Input
20095  * Bootstrap MonthField class
20096  * 
20097  * @cfg {String} language default en
20098  * 
20099  * @constructor
20100  * Create a new MonthField
20101  * @param {Object} config The config object
20102  */
20103
20104 Roo.bootstrap.MonthField = function(config){
20105     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20106     
20107     this.addEvents({
20108         /**
20109          * @event show
20110          * Fires when this field show.
20111          * @param {Roo.bootstrap.MonthField} this
20112          * @param {Mixed} date The date value
20113          */
20114         show : true,
20115         /**
20116          * @event show
20117          * Fires when this field hide.
20118          * @param {Roo.bootstrap.MonthField} this
20119          * @param {Mixed} date The date value
20120          */
20121         hide : true,
20122         /**
20123          * @event select
20124          * Fires when select a date.
20125          * @param {Roo.bootstrap.MonthField} this
20126          * @param {String} oldvalue The old value
20127          * @param {String} newvalue The new value
20128          */
20129         select : true
20130     });
20131 };
20132
20133 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20134     
20135     onRender: function(ct, position)
20136     {
20137         
20138         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20139         
20140         this.language = this.language || 'en';
20141         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20142         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20143         
20144         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20145         this.isInline = false;
20146         this.isInput = true;
20147         this.component = this.el.select('.add-on', true).first() || false;
20148         this.component = (this.component && this.component.length === 0) ? false : this.component;
20149         this.hasInput = this.component && this.inputEL().length;
20150         
20151         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20152         
20153         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20154         
20155         this.picker().on('mousedown', this.onMousedown, this);
20156         this.picker().on('click', this.onClick, this);
20157         
20158         this.picker().addClass('datepicker-dropdown');
20159         
20160         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20161             v.setStyle('width', '189px');
20162         });
20163         
20164         this.fillMonths();
20165         
20166         this.update();
20167         
20168         if(this.isInline) {
20169             this.show();
20170         }
20171         
20172     },
20173     
20174     setValue: function(v, suppressEvent)
20175     {   
20176         var o = this.getValue();
20177         
20178         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20179         
20180         this.update();
20181
20182         if(suppressEvent !== true){
20183             this.fireEvent('select', this, o, v);
20184         }
20185         
20186     },
20187     
20188     getValue: function()
20189     {
20190         return this.value;
20191     },
20192     
20193     onClick: function(e) 
20194     {
20195         e.stopPropagation();
20196         e.preventDefault();
20197         
20198         var target = e.getTarget();
20199         
20200         if(target.nodeName.toLowerCase() === 'i'){
20201             target = Roo.get(target).dom.parentNode;
20202         }
20203         
20204         var nodeName = target.nodeName;
20205         var className = target.className;
20206         var html = target.innerHTML;
20207         
20208         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20209             return;
20210         }
20211         
20212         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20213         
20214         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20215         
20216         this.hide();
20217                         
20218     },
20219     
20220     picker : function()
20221     {
20222         return this.pickerEl;
20223     },
20224     
20225     fillMonths: function()
20226     {    
20227         var i = 0;
20228         var months = this.picker().select('>.datepicker-months td', true).first();
20229         
20230         months.dom.innerHTML = '';
20231         
20232         while (i < 12) {
20233             var month = {
20234                 tag: 'span',
20235                 cls: 'month',
20236                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20237             };
20238             
20239             months.createChild(month);
20240         }
20241         
20242     },
20243     
20244     update: function()
20245     {
20246         var _this = this;
20247         
20248         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20249             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20250         }
20251         
20252         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20253             e.removeClass('active');
20254             
20255             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20256                 e.addClass('active');
20257             }
20258         })
20259     },
20260     
20261     place: function()
20262     {
20263         if(this.isInline) {
20264             return;
20265         }
20266         
20267         this.picker().removeClass(['bottom', 'top']);
20268         
20269         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20270             /*
20271              * place to the top of element!
20272              *
20273              */
20274             
20275             this.picker().addClass('top');
20276             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20277             
20278             return;
20279         }
20280         
20281         this.picker().addClass('bottom');
20282         
20283         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20284     },
20285     
20286     onFocus : function()
20287     {
20288         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20289         this.show();
20290     },
20291     
20292     onBlur : function()
20293     {
20294         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20295         
20296         var d = this.inputEl().getValue();
20297         
20298         this.setValue(d);
20299                 
20300         this.hide();
20301     },
20302     
20303     show : function()
20304     {
20305         this.picker().show();
20306         this.picker().select('>.datepicker-months', true).first().show();
20307         this.update();
20308         this.place();
20309         
20310         this.fireEvent('show', this, this.date);
20311     },
20312     
20313     hide : function()
20314     {
20315         if(this.isInline) {
20316             return;
20317         }
20318         this.picker().hide();
20319         this.fireEvent('hide', this, this.date);
20320         
20321     },
20322     
20323     onMousedown: function(e)
20324     {
20325         e.stopPropagation();
20326         e.preventDefault();
20327     },
20328     
20329     keyup: function(e)
20330     {
20331         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20332         this.update();
20333     },
20334
20335     fireKey: function(e)
20336     {
20337         if (!this.picker().isVisible()){
20338             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20339                 this.show();
20340             }
20341             return;
20342         }
20343         
20344         var dir;
20345         
20346         switch(e.keyCode){
20347             case 27: // escape
20348                 this.hide();
20349                 e.preventDefault();
20350                 break;
20351             case 37: // left
20352             case 39: // right
20353                 dir = e.keyCode == 37 ? -1 : 1;
20354                 
20355                 this.vIndex = this.vIndex + dir;
20356                 
20357                 if(this.vIndex < 0){
20358                     this.vIndex = 0;
20359                 }
20360                 
20361                 if(this.vIndex > 11){
20362                     this.vIndex = 11;
20363                 }
20364                 
20365                 if(isNaN(this.vIndex)){
20366                     this.vIndex = 0;
20367                 }
20368                 
20369                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20370                 
20371                 break;
20372             case 38: // up
20373             case 40: // down
20374                 
20375                 dir = e.keyCode == 38 ? -1 : 1;
20376                 
20377                 this.vIndex = this.vIndex + dir * 4;
20378                 
20379                 if(this.vIndex < 0){
20380                     this.vIndex = 0;
20381                 }
20382                 
20383                 if(this.vIndex > 11){
20384                     this.vIndex = 11;
20385                 }
20386                 
20387                 if(isNaN(this.vIndex)){
20388                     this.vIndex = 0;
20389                 }
20390                 
20391                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20392                 break;
20393                 
20394             case 13: // enter
20395                 
20396                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20397                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20398                 }
20399                 
20400                 this.hide();
20401                 e.preventDefault();
20402                 break;
20403             case 9: // tab
20404                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20405                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20406                 }
20407                 this.hide();
20408                 break;
20409             case 16: // shift
20410             case 17: // ctrl
20411             case 18: // alt
20412                 break;
20413             default :
20414                 this.hide();
20415                 
20416         }
20417     },
20418     
20419     remove: function() 
20420     {
20421         this.picker().remove();
20422     }
20423    
20424 });
20425
20426 Roo.apply(Roo.bootstrap.MonthField,  {
20427     
20428     content : {
20429         tag: 'tbody',
20430         cn: [
20431         {
20432             tag: 'tr',
20433             cn: [
20434             {
20435                 tag: 'td',
20436                 colspan: '7'
20437             }
20438             ]
20439         }
20440         ]
20441     },
20442     
20443     dates:{
20444         en: {
20445             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20446             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20447         }
20448     }
20449 });
20450
20451 Roo.apply(Roo.bootstrap.MonthField,  {
20452   
20453     template : {
20454         tag: 'div',
20455         cls: 'datepicker dropdown-menu roo-dynamic',
20456         cn: [
20457             {
20458                 tag: 'div',
20459                 cls: 'datepicker-months',
20460                 cn: [
20461                 {
20462                     tag: 'table',
20463                     cls: 'table-condensed',
20464                     cn:[
20465                         Roo.bootstrap.DateField.content
20466                     ]
20467                 }
20468                 ]
20469             }
20470         ]
20471     }
20472 });
20473
20474  
20475
20476  
20477  /*
20478  * - LGPL
20479  *
20480  * CheckBox
20481  * 
20482  */
20483
20484 /**
20485  * @class Roo.bootstrap.CheckBox
20486  * @extends Roo.bootstrap.Input
20487  * Bootstrap CheckBox class
20488  * 
20489  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20490  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20491  * @cfg {String} boxLabel The text that appears beside the checkbox
20492  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20493  * @cfg {Boolean} checked initnal the element
20494  * @cfg {Boolean} inline inline the element (default false)
20495  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20496  * @cfg {String} tooltip label tooltip
20497  * 
20498  * @constructor
20499  * Create a new CheckBox
20500  * @param {Object} config The config object
20501  */
20502
20503 Roo.bootstrap.CheckBox = function(config){
20504     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20505    
20506     this.addEvents({
20507         /**
20508         * @event check
20509         * Fires when the element is checked or unchecked.
20510         * @param {Roo.bootstrap.CheckBox} this This input
20511         * @param {Boolean} checked The new checked value
20512         */
20513        check : true,
20514        /**
20515         * @event click
20516         * Fires when the element is click.
20517         * @param {Roo.bootstrap.CheckBox} this This input
20518         */
20519        click : true
20520     });
20521     
20522 };
20523
20524 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20525   
20526     inputType: 'checkbox',
20527     inputValue: 1,
20528     valueOff: 0,
20529     boxLabel: false,
20530     checked: false,
20531     weight : false,
20532     inline: false,
20533     tooltip : '',
20534     
20535     getAutoCreate : function()
20536     {
20537         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20538         
20539         var id = Roo.id();
20540         
20541         var cfg = {};
20542         
20543         cfg.cls = 'form-group ' + this.inputType; //input-group
20544         
20545         if(this.inline){
20546             cfg.cls += ' ' + this.inputType + '-inline';
20547         }
20548         
20549         var input =  {
20550             tag: 'input',
20551             id : id,
20552             type : this.inputType,
20553             value : this.inputValue,
20554             cls : 'roo-' + this.inputType, //'form-box',
20555             placeholder : this.placeholder || ''
20556             
20557         };
20558         
20559         if(this.inputType != 'radio'){
20560             var hidden =  {
20561                 tag: 'input',
20562                 type : 'hidden',
20563                 cls : 'roo-hidden-value',
20564                 value : this.checked ? this.inputValue : this.valueOff
20565             };
20566         }
20567         
20568             
20569         if (this.weight) { // Validity check?
20570             cfg.cls += " " + this.inputType + "-" + this.weight;
20571         }
20572         
20573         if (this.disabled) {
20574             input.disabled=true;
20575         }
20576         
20577         if(this.checked){
20578             input.checked = this.checked;
20579         }
20580         
20581         if (this.name) {
20582             
20583             input.name = this.name;
20584             
20585             if(this.inputType != 'radio'){
20586                 hidden.name = this.name;
20587                 input.name = '_hidden_' + this.name;
20588             }
20589         }
20590         
20591         if (this.size) {
20592             input.cls += ' input-' + this.size;
20593         }
20594         
20595         var settings=this;
20596         
20597         ['xs','sm','md','lg'].map(function(size){
20598             if (settings[size]) {
20599                 cfg.cls += ' col-' + size + '-' + settings[size];
20600             }
20601         });
20602         
20603         var inputblock = input;
20604          
20605         if (this.before || this.after) {
20606             
20607             inputblock = {
20608                 cls : 'input-group',
20609                 cn :  [] 
20610             };
20611             
20612             if (this.before) {
20613                 inputblock.cn.push({
20614                     tag :'span',
20615                     cls : 'input-group-addon',
20616                     html : this.before
20617                 });
20618             }
20619             
20620             inputblock.cn.push(input);
20621             
20622             if(this.inputType != 'radio'){
20623                 inputblock.cn.push(hidden);
20624             }
20625             
20626             if (this.after) {
20627                 inputblock.cn.push({
20628                     tag :'span',
20629                     cls : 'input-group-addon',
20630                     html : this.after
20631                 });
20632             }
20633             
20634         }
20635         
20636         if (align ==='left' && this.fieldLabel.length) {
20637 //                Roo.log("left and has label");
20638             cfg.cn = [
20639                 {
20640                     tag: 'label',
20641                     'for' :  id,
20642                     cls : 'control-label',
20643                     html : this.fieldLabel
20644                 },
20645                 {
20646                     cls : "", 
20647                     cn: [
20648                         inputblock
20649                     ]
20650                 }
20651             ];
20652             
20653             if(this.labelWidth > 12){
20654                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20655             }
20656             
20657             if(this.labelWidth < 13 && this.labelmd == 0){
20658                 this.labelmd = this.labelWidth;
20659             }
20660             
20661             if(this.labellg > 0){
20662                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20663                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20664             }
20665             
20666             if(this.labelmd > 0){
20667                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20668                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20669             }
20670             
20671             if(this.labelsm > 0){
20672                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20673                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20674             }
20675             
20676             if(this.labelxs > 0){
20677                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20678                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20679             }
20680             
20681         } else if ( this.fieldLabel.length) {
20682 //                Roo.log(" label");
20683                 cfg.cn = [
20684                    
20685                     {
20686                         tag: this.boxLabel ? 'span' : 'label',
20687                         'for': id,
20688                         cls: 'control-label box-input-label',
20689                         //cls : 'input-group-addon',
20690                         html : this.fieldLabel
20691                     },
20692                     
20693                     inputblock
20694                     
20695                 ];
20696
20697         } else {
20698             
20699 //                Roo.log(" no label && no align");
20700                 cfg.cn = [  inputblock ] ;
20701                 
20702                 
20703         }
20704         
20705         if(this.boxLabel){
20706              var boxLabelCfg = {
20707                 tag: 'label',
20708                 //'for': id, // box label is handled by onclick - so no for...
20709                 cls: 'box-label',
20710                 html: this.boxLabel
20711             };
20712             
20713             if(this.tooltip){
20714                 boxLabelCfg.tooltip = this.tooltip;
20715             }
20716              
20717             cfg.cn.push(boxLabelCfg);
20718         }
20719         
20720         if(this.inputType != 'radio'){
20721             cfg.cn.push(hidden);
20722         }
20723         
20724         return cfg;
20725         
20726     },
20727     
20728     /**
20729      * return the real input element.
20730      */
20731     inputEl: function ()
20732     {
20733         return this.el.select('input.roo-' + this.inputType,true).first();
20734     },
20735     hiddenEl: function ()
20736     {
20737         return this.el.select('input.roo-hidden-value',true).first();
20738     },
20739     
20740     labelEl: function()
20741     {
20742         return this.el.select('label.control-label',true).first();
20743     },
20744     /* depricated... */
20745     
20746     label: function()
20747     {
20748         return this.labelEl();
20749     },
20750     
20751     boxLabelEl: function()
20752     {
20753         return this.el.select('label.box-label',true).first();
20754     },
20755     
20756     initEvents : function()
20757     {
20758 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20759         
20760         this.inputEl().on('click', this.onClick,  this);
20761         
20762         if (this.boxLabel) { 
20763             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20764         }
20765         
20766         this.startValue = this.getValue();
20767         
20768         if(this.groupId){
20769             Roo.bootstrap.CheckBox.register(this);
20770         }
20771     },
20772     
20773     onClick : function(e)
20774     {   
20775         if(this.fireEvent('click', this, e) !== false){
20776             this.setChecked(!this.checked);
20777         }
20778         
20779     },
20780     
20781     setChecked : function(state,suppressEvent)
20782     {
20783         this.startValue = this.getValue();
20784
20785         if(this.inputType == 'radio'){
20786             
20787             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20788                 e.dom.checked = false;
20789             });
20790             
20791             this.inputEl().dom.checked = true;
20792             
20793             this.inputEl().dom.value = this.inputValue;
20794             
20795             if(suppressEvent !== true){
20796                 this.fireEvent('check', this, true);
20797             }
20798             
20799             this.validate();
20800             
20801             return;
20802         }
20803         
20804         this.checked = state;
20805         
20806         this.inputEl().dom.checked = state;
20807         
20808         
20809         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20810         
20811         if(suppressEvent !== true){
20812             this.fireEvent('check', this, state);
20813         }
20814         
20815         this.validate();
20816     },
20817     
20818     getValue : function()
20819     {
20820         if(this.inputType == 'radio'){
20821             return this.getGroupValue();
20822         }
20823         
20824         return this.hiddenEl().dom.value;
20825         
20826     },
20827     
20828     getGroupValue : function()
20829     {
20830         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20831             return '';
20832         }
20833         
20834         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20835     },
20836     
20837     setValue : function(v,suppressEvent)
20838     {
20839         if(this.inputType == 'radio'){
20840             this.setGroupValue(v, suppressEvent);
20841             return;
20842         }
20843         
20844         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20845         
20846         this.validate();
20847     },
20848     
20849     setGroupValue : function(v, suppressEvent)
20850     {
20851         this.startValue = this.getValue();
20852         
20853         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20854             e.dom.checked = false;
20855             
20856             if(e.dom.value == v){
20857                 e.dom.checked = true;
20858             }
20859         });
20860         
20861         if(suppressEvent !== true){
20862             this.fireEvent('check', this, true);
20863         }
20864
20865         this.validate();
20866         
20867         return;
20868     },
20869     
20870     validate : function()
20871     {
20872         if(this.getVisibilityEl().hasClass('hidden')){
20873             return true;
20874         }
20875         
20876         if(
20877                 this.disabled || 
20878                 (this.inputType == 'radio' && this.validateRadio()) ||
20879                 (this.inputType == 'checkbox' && this.validateCheckbox())
20880         ){
20881             this.markValid();
20882             return true;
20883         }
20884         
20885         this.markInvalid();
20886         return false;
20887     },
20888     
20889     validateRadio : function()
20890     {
20891         if(this.getVisibilityEl().hasClass('hidden')){
20892             return true;
20893         }
20894         
20895         if(this.allowBlank){
20896             return true;
20897         }
20898         
20899         var valid = false;
20900         
20901         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20902             if(!e.dom.checked){
20903                 return;
20904             }
20905             
20906             valid = true;
20907             
20908             return false;
20909         });
20910         
20911         return valid;
20912     },
20913     
20914     validateCheckbox : function()
20915     {
20916         if(!this.groupId){
20917             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20918             //return (this.getValue() == this.inputValue) ? true : false;
20919         }
20920         
20921         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20922         
20923         if(!group){
20924             return false;
20925         }
20926         
20927         var r = false;
20928         
20929         for(var i in group){
20930             if(group[i].el.isVisible(true)){
20931                 r = false;
20932                 break;
20933             }
20934             
20935             r = true;
20936         }
20937         
20938         for(var i in group){
20939             if(r){
20940                 break;
20941             }
20942             
20943             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20944         }
20945         
20946         return r;
20947     },
20948     
20949     /**
20950      * Mark this field as valid
20951      */
20952     markValid : function()
20953     {
20954         var _this = this;
20955         
20956         this.fireEvent('valid', this);
20957         
20958         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20959         
20960         if(this.groupId){
20961             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20962         }
20963         
20964         if(label){
20965             label.markValid();
20966         }
20967
20968         if(this.inputType == 'radio'){
20969             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20970                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20971                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20972             });
20973             
20974             return;
20975         }
20976
20977         if(!this.groupId){
20978             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20979             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20980             return;
20981         }
20982         
20983         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20984         
20985         if(!group){
20986             return;
20987         }
20988         
20989         for(var i in group){
20990             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20991             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20992         }
20993     },
20994     
20995      /**
20996      * Mark this field as invalid
20997      * @param {String} msg The validation message
20998      */
20999     markInvalid : function(msg)
21000     {
21001         if(this.allowBlank){
21002             return;
21003         }
21004         
21005         var _this = this;
21006         
21007         this.fireEvent('invalid', this, msg);
21008         
21009         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21010         
21011         if(this.groupId){
21012             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21013         }
21014         
21015         if(label){
21016             label.markInvalid();
21017         }
21018             
21019         if(this.inputType == 'radio'){
21020             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21021                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21022                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21023             });
21024             
21025             return;
21026         }
21027         
21028         if(!this.groupId){
21029             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21030             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21031             return;
21032         }
21033         
21034         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21035         
21036         if(!group){
21037             return;
21038         }
21039         
21040         for(var i in group){
21041             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21042             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21043         }
21044         
21045     },
21046     
21047     clearInvalid : function()
21048     {
21049         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21050         
21051         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21052         
21053         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21054         
21055         if (label && label.iconEl) {
21056             label.iconEl.removeClass(label.validClass);
21057             label.iconEl.removeClass(label.invalidClass);
21058         }
21059     },
21060     
21061     disable : function()
21062     {
21063         if(this.inputType != 'radio'){
21064             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21065             return;
21066         }
21067         
21068         var _this = this;
21069         
21070         if(this.rendered){
21071             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21072                 _this.getActionEl().addClass(this.disabledClass);
21073                 e.dom.disabled = true;
21074             });
21075         }
21076         
21077         this.disabled = true;
21078         this.fireEvent("disable", this);
21079         return this;
21080     },
21081
21082     enable : function()
21083     {
21084         if(this.inputType != 'radio'){
21085             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21086             return;
21087         }
21088         
21089         var _this = this;
21090         
21091         if(this.rendered){
21092             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21093                 _this.getActionEl().removeClass(this.disabledClass);
21094                 e.dom.disabled = false;
21095             });
21096         }
21097         
21098         this.disabled = false;
21099         this.fireEvent("enable", this);
21100         return this;
21101     },
21102     
21103     setBoxLabel : function(v)
21104     {
21105         this.boxLabel = v;
21106         
21107         if(this.rendered){
21108             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21109         }
21110     }
21111
21112 });
21113
21114 Roo.apply(Roo.bootstrap.CheckBox, {
21115     
21116     groups: {},
21117     
21118      /**
21119     * register a CheckBox Group
21120     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21121     */
21122     register : function(checkbox)
21123     {
21124         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21125             this.groups[checkbox.groupId] = {};
21126         }
21127         
21128         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21129             return;
21130         }
21131         
21132         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21133         
21134     },
21135     /**
21136     * fetch a CheckBox Group based on the group ID
21137     * @param {string} the group ID
21138     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21139     */
21140     get: function(groupId) {
21141         if (typeof(this.groups[groupId]) == 'undefined') {
21142             return false;
21143         }
21144         
21145         return this.groups[groupId] ;
21146     }
21147     
21148     
21149 });
21150 /*
21151  * - LGPL
21152  *
21153  * RadioItem
21154  * 
21155  */
21156
21157 /**
21158  * @class Roo.bootstrap.Radio
21159  * @extends Roo.bootstrap.Component
21160  * Bootstrap Radio class
21161  * @cfg {String} boxLabel - the label associated
21162  * @cfg {String} value - the value of radio
21163  * 
21164  * @constructor
21165  * Create a new Radio
21166  * @param {Object} config The config object
21167  */
21168 Roo.bootstrap.Radio = function(config){
21169     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21170     
21171 };
21172
21173 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21174     
21175     boxLabel : '',
21176     
21177     value : '',
21178     
21179     getAutoCreate : function()
21180     {
21181         var cfg = {
21182             tag : 'div',
21183             cls : 'form-group radio',
21184             cn : [
21185                 {
21186                     tag : 'label',
21187                     cls : 'box-label',
21188                     html : this.boxLabel
21189                 }
21190             ]
21191         };
21192         
21193         return cfg;
21194     },
21195     
21196     initEvents : function() 
21197     {
21198         this.parent().register(this);
21199         
21200         this.el.on('click', this.onClick, this);
21201         
21202     },
21203     
21204     onClick : function(e)
21205     {
21206         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21207             this.setChecked(true);
21208         }
21209     },
21210     
21211     setChecked : function(state, suppressEvent)
21212     {
21213         this.parent().setValue(this.value, suppressEvent);
21214         
21215     },
21216     
21217     setBoxLabel : function(v)
21218     {
21219         this.boxLabel = v;
21220         
21221         if(this.rendered){
21222             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21223         }
21224     }
21225     
21226 });
21227  
21228
21229  /*
21230  * - LGPL
21231  *
21232  * Input
21233  * 
21234  */
21235
21236 /**
21237  * @class Roo.bootstrap.SecurePass
21238  * @extends Roo.bootstrap.Input
21239  * Bootstrap SecurePass class
21240  *
21241  * 
21242  * @constructor
21243  * Create a new SecurePass
21244  * @param {Object} config The config object
21245  */
21246  
21247 Roo.bootstrap.SecurePass = function (config) {
21248     // these go here, so the translation tool can replace them..
21249     this.errors = {
21250         PwdEmpty: "Please type a password, and then retype it to confirm.",
21251         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21252         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21253         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21254         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21255         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21256         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21257         TooWeak: "Your password is Too Weak."
21258     },
21259     this.meterLabel = "Password strength:";
21260     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21261     this.meterClass = [
21262         "roo-password-meter-tooweak", 
21263         "roo-password-meter-weak", 
21264         "roo-password-meter-medium", 
21265         "roo-password-meter-strong", 
21266         "roo-password-meter-grey"
21267     ];
21268     
21269     this.errors = {};
21270     
21271     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21272 }
21273
21274 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21275     /**
21276      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21277      * {
21278      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21279      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21280      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21281      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21282      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21283      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21284      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21285      * })
21286      */
21287     // private
21288     
21289     meterWidth: 300,
21290     errorMsg :'',    
21291     errors: false,
21292     imageRoot: '/',
21293     /**
21294      * @cfg {String/Object} Label for the strength meter (defaults to
21295      * 'Password strength:')
21296      */
21297     // private
21298     meterLabel: '',
21299     /**
21300      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21301      * ['Weak', 'Medium', 'Strong'])
21302      */
21303     // private    
21304     pwdStrengths: false,    
21305     // private
21306     strength: 0,
21307     // private
21308     _lastPwd: null,
21309     // private
21310     kCapitalLetter: 0,
21311     kSmallLetter: 1,
21312     kDigit: 2,
21313     kPunctuation: 3,
21314     
21315     insecure: false,
21316     // private
21317     initEvents: function ()
21318     {
21319         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21320
21321         if (this.el.is('input[type=password]') && Roo.isSafari) {
21322             this.el.on('keydown', this.SafariOnKeyDown, this);
21323         }
21324
21325         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21326     },
21327     // private
21328     onRender: function (ct, position)
21329     {
21330         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21331         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21332         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21333
21334         this.trigger.createChild({
21335                    cn: [
21336                     {
21337                     //id: 'PwdMeter',
21338                     tag: 'div',
21339                     cls: 'roo-password-meter-grey col-xs-12',
21340                     style: {
21341                         //width: 0,
21342                         //width: this.meterWidth + 'px'                                                
21343                         }
21344                     },
21345                     {                            
21346                          cls: 'roo-password-meter-text'                          
21347                     }
21348                 ]            
21349         });
21350
21351          
21352         if (this.hideTrigger) {
21353             this.trigger.setDisplayed(false);
21354         }
21355         this.setSize(this.width || '', this.height || '');
21356     },
21357     // private
21358     onDestroy: function ()
21359     {
21360         if (this.trigger) {
21361             this.trigger.removeAllListeners();
21362             this.trigger.remove();
21363         }
21364         if (this.wrap) {
21365             this.wrap.remove();
21366         }
21367         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21368     },
21369     // private
21370     checkStrength: function ()
21371     {
21372         var pwd = this.inputEl().getValue();
21373         if (pwd == this._lastPwd) {
21374             return;
21375         }
21376
21377         var strength;
21378         if (this.ClientSideStrongPassword(pwd)) {
21379             strength = 3;
21380         } else if (this.ClientSideMediumPassword(pwd)) {
21381             strength = 2;
21382         } else if (this.ClientSideWeakPassword(pwd)) {
21383             strength = 1;
21384         } else {
21385             strength = 0;
21386         }
21387         
21388         Roo.log('strength1: ' + strength);
21389         
21390         //var pm = this.trigger.child('div/div/div').dom;
21391         var pm = this.trigger.child('div/div');
21392         pm.removeClass(this.meterClass);
21393         pm.addClass(this.meterClass[strength]);
21394                 
21395         
21396         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21397                 
21398         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21399         
21400         this._lastPwd = pwd;
21401     },
21402     reset: function ()
21403     {
21404         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21405         
21406         this._lastPwd = '';
21407         
21408         var pm = this.trigger.child('div/div');
21409         pm.removeClass(this.meterClass);
21410         pm.addClass('roo-password-meter-grey');        
21411         
21412         
21413         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21414         
21415         pt.innerHTML = '';
21416         this.inputEl().dom.type='password';
21417     },
21418     // private
21419     validateValue: function (value)
21420     {
21421         
21422         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21423             return false;
21424         }
21425         if (value.length == 0) {
21426             if (this.allowBlank) {
21427                 this.clearInvalid();
21428                 return true;
21429             }
21430
21431             this.markInvalid(this.errors.PwdEmpty);
21432             this.errorMsg = this.errors.PwdEmpty;
21433             return false;
21434         }
21435         
21436         if(this.insecure){
21437             return true;
21438         }
21439         
21440         if ('[\x21-\x7e]*'.match(value)) {
21441             this.markInvalid(this.errors.PwdBadChar);
21442             this.errorMsg = this.errors.PwdBadChar;
21443             return false;
21444         }
21445         if (value.length < 6) {
21446             this.markInvalid(this.errors.PwdShort);
21447             this.errorMsg = this.errors.PwdShort;
21448             return false;
21449         }
21450         if (value.length > 16) {
21451             this.markInvalid(this.errors.PwdLong);
21452             this.errorMsg = this.errors.PwdLong;
21453             return false;
21454         }
21455         var strength;
21456         if (this.ClientSideStrongPassword(value)) {
21457             strength = 3;
21458         } else if (this.ClientSideMediumPassword(value)) {
21459             strength = 2;
21460         } else if (this.ClientSideWeakPassword(value)) {
21461             strength = 1;
21462         } else {
21463             strength = 0;
21464         }
21465
21466         
21467         if (strength < 2) {
21468             //this.markInvalid(this.errors.TooWeak);
21469             this.errorMsg = this.errors.TooWeak;
21470             //return false;
21471         }
21472         
21473         
21474         console.log('strength2: ' + strength);
21475         
21476         //var pm = this.trigger.child('div/div/div').dom;
21477         
21478         var pm = this.trigger.child('div/div');
21479         pm.removeClass(this.meterClass);
21480         pm.addClass(this.meterClass[strength]);
21481                 
21482         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21483                 
21484         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21485         
21486         this.errorMsg = ''; 
21487         return true;
21488     },
21489     // private
21490     CharacterSetChecks: function (type)
21491     {
21492         this.type = type;
21493         this.fResult = false;
21494     },
21495     // private
21496     isctype: function (character, type)
21497     {
21498         switch (type) {  
21499             case this.kCapitalLetter:
21500                 if (character >= 'A' && character <= 'Z') {
21501                     return true;
21502                 }
21503                 break;
21504             
21505             case this.kSmallLetter:
21506                 if (character >= 'a' && character <= 'z') {
21507                     return true;
21508                 }
21509                 break;
21510             
21511             case this.kDigit:
21512                 if (character >= '0' && character <= '9') {
21513                     return true;
21514                 }
21515                 break;
21516             
21517             case this.kPunctuation:
21518                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21519                     return true;
21520                 }
21521                 break;
21522             
21523             default:
21524                 return false;
21525         }
21526
21527     },
21528     // private
21529     IsLongEnough: function (pwd, size)
21530     {
21531         return !(pwd == null || isNaN(size) || pwd.length < size);
21532     },
21533     // private
21534     SpansEnoughCharacterSets: function (word, nb)
21535     {
21536         if (!this.IsLongEnough(word, nb))
21537         {
21538             return false;
21539         }
21540
21541         var characterSetChecks = new Array(
21542             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21543             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21544         );
21545         
21546         for (var index = 0; index < word.length; ++index) {
21547             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21548                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21549                     characterSetChecks[nCharSet].fResult = true;
21550                     break;
21551                 }
21552             }
21553         }
21554
21555         var nCharSets = 0;
21556         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21557             if (characterSetChecks[nCharSet].fResult) {
21558                 ++nCharSets;
21559             }
21560         }
21561
21562         if (nCharSets < nb) {
21563             return false;
21564         }
21565         return true;
21566     },
21567     // private
21568     ClientSideStrongPassword: function (pwd)
21569     {
21570         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21571     },
21572     // private
21573     ClientSideMediumPassword: function (pwd)
21574     {
21575         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21576     },
21577     // private
21578     ClientSideWeakPassword: function (pwd)
21579     {
21580         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21581     }
21582           
21583 })//<script type="text/javascript">
21584
21585 /*
21586  * Based  Ext JS Library 1.1.1
21587  * Copyright(c) 2006-2007, Ext JS, LLC.
21588  * LGPL
21589  *
21590  */
21591  
21592 /**
21593  * @class Roo.HtmlEditorCore
21594  * @extends Roo.Component
21595  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21596  *
21597  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21598  */
21599
21600 Roo.HtmlEditorCore = function(config){
21601     
21602     
21603     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21604     
21605     
21606     this.addEvents({
21607         /**
21608          * @event initialize
21609          * Fires when the editor is fully initialized (including the iframe)
21610          * @param {Roo.HtmlEditorCore} this
21611          */
21612         initialize: true,
21613         /**
21614          * @event activate
21615          * Fires when the editor is first receives the focus. Any insertion must wait
21616          * until after this event.
21617          * @param {Roo.HtmlEditorCore} this
21618          */
21619         activate: true,
21620          /**
21621          * @event beforesync
21622          * Fires before the textarea is updated with content from the editor iframe. Return false
21623          * to cancel the sync.
21624          * @param {Roo.HtmlEditorCore} this
21625          * @param {String} html
21626          */
21627         beforesync: true,
21628          /**
21629          * @event beforepush
21630          * Fires before the iframe editor is updated with content from the textarea. Return false
21631          * to cancel the push.
21632          * @param {Roo.HtmlEditorCore} this
21633          * @param {String} html
21634          */
21635         beforepush: true,
21636          /**
21637          * @event sync
21638          * Fires when the textarea is updated with content from the editor iframe.
21639          * @param {Roo.HtmlEditorCore} this
21640          * @param {String} html
21641          */
21642         sync: true,
21643          /**
21644          * @event push
21645          * Fires when the iframe editor is updated with content from the textarea.
21646          * @param {Roo.HtmlEditorCore} this
21647          * @param {String} html
21648          */
21649         push: true,
21650         
21651         /**
21652          * @event editorevent
21653          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21654          * @param {Roo.HtmlEditorCore} this
21655          */
21656         editorevent: true
21657         
21658     });
21659     
21660     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21661     
21662     // defaults : white / black...
21663     this.applyBlacklists();
21664     
21665     
21666     
21667 };
21668
21669
21670 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21671
21672
21673      /**
21674      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21675      */
21676     
21677     owner : false,
21678     
21679      /**
21680      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21681      *                        Roo.resizable.
21682      */
21683     resizable : false,
21684      /**
21685      * @cfg {Number} height (in pixels)
21686      */   
21687     height: 300,
21688    /**
21689      * @cfg {Number} width (in pixels)
21690      */   
21691     width: 500,
21692     
21693     /**
21694      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21695      * 
21696      */
21697     stylesheets: false,
21698     
21699     // id of frame..
21700     frameId: false,
21701     
21702     // private properties
21703     validationEvent : false,
21704     deferHeight: true,
21705     initialized : false,
21706     activated : false,
21707     sourceEditMode : false,
21708     onFocus : Roo.emptyFn,
21709     iframePad:3,
21710     hideMode:'offsets',
21711     
21712     clearUp: true,
21713     
21714     // blacklist + whitelisted elements..
21715     black: false,
21716     white: false,
21717      
21718     bodyCls : '',
21719
21720     /**
21721      * Protected method that will not generally be called directly. It
21722      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21723      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21724      */
21725     getDocMarkup : function(){
21726         // body styles..
21727         var st = '';
21728         
21729         // inherit styels from page...?? 
21730         if (this.stylesheets === false) {
21731             
21732             Roo.get(document.head).select('style').each(function(node) {
21733                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21734             });
21735             
21736             Roo.get(document.head).select('link').each(function(node) { 
21737                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21738             });
21739             
21740         } else if (!this.stylesheets.length) {
21741                 // simple..
21742                 st = '<style type="text/css">' +
21743                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21744                    '</style>';
21745         } else { 
21746             st = '<style type="text/css">' +
21747                     this.stylesheets +
21748                 '</style>';
21749         }
21750         
21751         st +=  '<style type="text/css">' +
21752             'IMG { cursor: pointer } ' +
21753         '</style>';
21754
21755         var cls = 'roo-htmleditor-body';
21756         
21757         if(this.bodyCls.length){
21758             cls += ' ' + this.bodyCls;
21759         }
21760         
21761         return '<html><head>' + st  +
21762             //<style type="text/css">' +
21763             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21764             //'</style>' +
21765             ' </head><body class="' +  cls + '"></body></html>';
21766     },
21767
21768     // private
21769     onRender : function(ct, position)
21770     {
21771         var _t = this;
21772         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21773         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21774         
21775         
21776         this.el.dom.style.border = '0 none';
21777         this.el.dom.setAttribute('tabIndex', -1);
21778         this.el.addClass('x-hidden hide');
21779         
21780         
21781         
21782         if(Roo.isIE){ // fix IE 1px bogus margin
21783             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21784         }
21785        
21786         
21787         this.frameId = Roo.id();
21788         
21789          
21790         
21791         var iframe = this.owner.wrap.createChild({
21792             tag: 'iframe',
21793             cls: 'form-control', // bootstrap..
21794             id: this.frameId,
21795             name: this.frameId,
21796             frameBorder : 'no',
21797             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21798         }, this.el
21799         );
21800         
21801         
21802         this.iframe = iframe.dom;
21803
21804          this.assignDocWin();
21805         
21806         this.doc.designMode = 'on';
21807        
21808         this.doc.open();
21809         this.doc.write(this.getDocMarkup());
21810         this.doc.close();
21811
21812         
21813         var task = { // must defer to wait for browser to be ready
21814             run : function(){
21815                 //console.log("run task?" + this.doc.readyState);
21816                 this.assignDocWin();
21817                 if(this.doc.body || this.doc.readyState == 'complete'){
21818                     try {
21819                         this.doc.designMode="on";
21820                     } catch (e) {
21821                         return;
21822                     }
21823                     Roo.TaskMgr.stop(task);
21824                     this.initEditor.defer(10, this);
21825                 }
21826             },
21827             interval : 10,
21828             duration: 10000,
21829             scope: this
21830         };
21831         Roo.TaskMgr.start(task);
21832
21833     },
21834
21835     // private
21836     onResize : function(w, h)
21837     {
21838          Roo.log('resize: ' +w + ',' + h );
21839         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21840         if(!this.iframe){
21841             return;
21842         }
21843         if(typeof w == 'number'){
21844             
21845             this.iframe.style.width = w + 'px';
21846         }
21847         if(typeof h == 'number'){
21848             
21849             this.iframe.style.height = h + 'px';
21850             if(this.doc){
21851                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21852             }
21853         }
21854         
21855     },
21856
21857     /**
21858      * Toggles the editor between standard and source edit mode.
21859      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21860      */
21861     toggleSourceEdit : function(sourceEditMode){
21862         
21863         this.sourceEditMode = sourceEditMode === true;
21864         
21865         if(this.sourceEditMode){
21866  
21867             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21868             
21869         }else{
21870             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21871             //this.iframe.className = '';
21872             this.deferFocus();
21873         }
21874         //this.setSize(this.owner.wrap.getSize());
21875         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21876     },
21877
21878     
21879   
21880
21881     /**
21882      * Protected method that will not generally be called directly. If you need/want
21883      * custom HTML cleanup, this is the method you should override.
21884      * @param {String} html The HTML to be cleaned
21885      * return {String} The cleaned HTML
21886      */
21887     cleanHtml : function(html){
21888         html = String(html);
21889         if(html.length > 5){
21890             if(Roo.isSafari){ // strip safari nonsense
21891                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21892             }
21893         }
21894         if(html == '&nbsp;'){
21895             html = '';
21896         }
21897         return html;
21898     },
21899
21900     /**
21901      * HTML Editor -> Textarea
21902      * Protected method that will not generally be called directly. Syncs the contents
21903      * of the editor iframe with the textarea.
21904      */
21905     syncValue : function(){
21906         if(this.initialized){
21907             var bd = (this.doc.body || this.doc.documentElement);
21908             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21909             var html = bd.innerHTML;
21910             if(Roo.isSafari){
21911                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21912                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21913                 if(m && m[1]){
21914                     html = '<div style="'+m[0]+'">' + html + '</div>';
21915                 }
21916             }
21917             html = this.cleanHtml(html);
21918             // fix up the special chars.. normaly like back quotes in word...
21919             // however we do not want to do this with chinese..
21920             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21921                 var cc = b.charCodeAt();
21922                 if (
21923                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21924                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21925                     (cc >= 0xf900 && cc < 0xfb00 )
21926                 ) {
21927                         return b;
21928                 }
21929                 return "&#"+cc+";" 
21930             });
21931             if(this.owner.fireEvent('beforesync', this, html) !== false){
21932                 this.el.dom.value = html;
21933                 this.owner.fireEvent('sync', this, html);
21934             }
21935         }
21936     },
21937
21938     /**
21939      * Protected method that will not generally be called directly. Pushes the value of the textarea
21940      * into the iframe editor.
21941      */
21942     pushValue : function(){
21943         if(this.initialized){
21944             var v = this.el.dom.value.trim();
21945             
21946 //            if(v.length < 1){
21947 //                v = '&#160;';
21948 //            }
21949             
21950             if(this.owner.fireEvent('beforepush', this, v) !== false){
21951                 var d = (this.doc.body || this.doc.documentElement);
21952                 d.innerHTML = v;
21953                 this.cleanUpPaste();
21954                 this.el.dom.value = d.innerHTML;
21955                 this.owner.fireEvent('push', this, v);
21956             }
21957         }
21958     },
21959
21960     // private
21961     deferFocus : function(){
21962         this.focus.defer(10, this);
21963     },
21964
21965     // doc'ed in Field
21966     focus : function(){
21967         if(this.win && !this.sourceEditMode){
21968             this.win.focus();
21969         }else{
21970             this.el.focus();
21971         }
21972     },
21973     
21974     assignDocWin: function()
21975     {
21976         var iframe = this.iframe;
21977         
21978          if(Roo.isIE){
21979             this.doc = iframe.contentWindow.document;
21980             this.win = iframe.contentWindow;
21981         } else {
21982 //            if (!Roo.get(this.frameId)) {
21983 //                return;
21984 //            }
21985 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21986 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21987             
21988             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21989                 return;
21990             }
21991             
21992             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21993             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21994         }
21995     },
21996     
21997     // private
21998     initEditor : function(){
21999         //console.log("INIT EDITOR");
22000         this.assignDocWin();
22001         
22002         
22003         
22004         this.doc.designMode="on";
22005         this.doc.open();
22006         this.doc.write(this.getDocMarkup());
22007         this.doc.close();
22008         
22009         var dbody = (this.doc.body || this.doc.documentElement);
22010         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22011         // this copies styles from the containing element into thsi one..
22012         // not sure why we need all of this..
22013         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22014         
22015         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22016         //ss['background-attachment'] = 'fixed'; // w3c
22017         dbody.bgProperties = 'fixed'; // ie
22018         //Roo.DomHelper.applyStyles(dbody, ss);
22019         Roo.EventManager.on(this.doc, {
22020             //'mousedown': this.onEditorEvent,
22021             'mouseup': this.onEditorEvent,
22022             'dblclick': this.onEditorEvent,
22023             'click': this.onEditorEvent,
22024             'keyup': this.onEditorEvent,
22025             buffer:100,
22026             scope: this
22027         });
22028         if(Roo.isGecko){
22029             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22030         }
22031         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22032             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22033         }
22034         this.initialized = true;
22035
22036         this.owner.fireEvent('initialize', this);
22037         this.pushValue();
22038     },
22039
22040     // private
22041     onDestroy : function(){
22042         
22043         
22044         
22045         if(this.rendered){
22046             
22047             //for (var i =0; i < this.toolbars.length;i++) {
22048             //    // fixme - ask toolbars for heights?
22049             //    this.toolbars[i].onDestroy();
22050            // }
22051             
22052             //this.wrap.dom.innerHTML = '';
22053             //this.wrap.remove();
22054         }
22055     },
22056
22057     // private
22058     onFirstFocus : function(){
22059         
22060         this.assignDocWin();
22061         
22062         
22063         this.activated = true;
22064          
22065     
22066         if(Roo.isGecko){ // prevent silly gecko errors
22067             this.win.focus();
22068             var s = this.win.getSelection();
22069             if(!s.focusNode || s.focusNode.nodeType != 3){
22070                 var r = s.getRangeAt(0);
22071                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22072                 r.collapse(true);
22073                 this.deferFocus();
22074             }
22075             try{
22076                 this.execCmd('useCSS', true);
22077                 this.execCmd('styleWithCSS', false);
22078             }catch(e){}
22079         }
22080         this.owner.fireEvent('activate', this);
22081     },
22082
22083     // private
22084     adjustFont: function(btn){
22085         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22086         //if(Roo.isSafari){ // safari
22087         //    adjust *= 2;
22088        // }
22089         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22090         if(Roo.isSafari){ // safari
22091             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22092             v =  (v < 10) ? 10 : v;
22093             v =  (v > 48) ? 48 : v;
22094             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22095             
22096         }
22097         
22098         
22099         v = Math.max(1, v+adjust);
22100         
22101         this.execCmd('FontSize', v  );
22102     },
22103
22104     onEditorEvent : function(e)
22105     {
22106         this.owner.fireEvent('editorevent', this, e);
22107       //  this.updateToolbar();
22108         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22109     },
22110
22111     insertTag : function(tg)
22112     {
22113         // could be a bit smarter... -> wrap the current selected tRoo..
22114         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22115             
22116             range = this.createRange(this.getSelection());
22117             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22118             wrappingNode.appendChild(range.extractContents());
22119             range.insertNode(wrappingNode);
22120
22121             return;
22122             
22123             
22124             
22125         }
22126         this.execCmd("formatblock",   tg);
22127         
22128     },
22129     
22130     insertText : function(txt)
22131     {
22132         
22133         
22134         var range = this.createRange();
22135         range.deleteContents();
22136                //alert(Sender.getAttribute('label'));
22137                
22138         range.insertNode(this.doc.createTextNode(txt));
22139     } ,
22140     
22141      
22142
22143     /**
22144      * Executes a Midas editor command on the editor document and performs necessary focus and
22145      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22146      * @param {String} cmd The Midas command
22147      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22148      */
22149     relayCmd : function(cmd, value){
22150         this.win.focus();
22151         this.execCmd(cmd, value);
22152         this.owner.fireEvent('editorevent', this);
22153         //this.updateToolbar();
22154         this.owner.deferFocus();
22155     },
22156
22157     /**
22158      * Executes a Midas editor command directly on the editor document.
22159      * For visual commands, you should use {@link #relayCmd} instead.
22160      * <b>This should only be called after the editor is initialized.</b>
22161      * @param {String} cmd The Midas command
22162      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22163      */
22164     execCmd : function(cmd, value){
22165         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22166         this.syncValue();
22167     },
22168  
22169  
22170    
22171     /**
22172      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22173      * to insert tRoo.
22174      * @param {String} text | dom node.. 
22175      */
22176     insertAtCursor : function(text)
22177     {
22178         
22179         if(!this.activated){
22180             return;
22181         }
22182         /*
22183         if(Roo.isIE){
22184             this.win.focus();
22185             var r = this.doc.selection.createRange();
22186             if(r){
22187                 r.collapse(true);
22188                 r.pasteHTML(text);
22189                 this.syncValue();
22190                 this.deferFocus();
22191             
22192             }
22193             return;
22194         }
22195         */
22196         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22197             this.win.focus();
22198             
22199             
22200             // from jquery ui (MIT licenced)
22201             var range, node;
22202             var win = this.win;
22203             
22204             if (win.getSelection && win.getSelection().getRangeAt) {
22205                 range = win.getSelection().getRangeAt(0);
22206                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22207                 range.insertNode(node);
22208             } else if (win.document.selection && win.document.selection.createRange) {
22209                 // no firefox support
22210                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22211                 win.document.selection.createRange().pasteHTML(txt);
22212             } else {
22213                 // no firefox support
22214                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22215                 this.execCmd('InsertHTML', txt);
22216             } 
22217             
22218             this.syncValue();
22219             
22220             this.deferFocus();
22221         }
22222     },
22223  // private
22224     mozKeyPress : function(e){
22225         if(e.ctrlKey){
22226             var c = e.getCharCode(), cmd;
22227           
22228             if(c > 0){
22229                 c = String.fromCharCode(c).toLowerCase();
22230                 switch(c){
22231                     case 'b':
22232                         cmd = 'bold';
22233                         break;
22234                     case 'i':
22235                         cmd = 'italic';
22236                         break;
22237                     
22238                     case 'u':
22239                         cmd = 'underline';
22240                         break;
22241                     
22242                     case 'v':
22243                         this.cleanUpPaste.defer(100, this);
22244                         return;
22245                         
22246                 }
22247                 if(cmd){
22248                     this.win.focus();
22249                     this.execCmd(cmd);
22250                     this.deferFocus();
22251                     e.preventDefault();
22252                 }
22253                 
22254             }
22255         }
22256     },
22257
22258     // private
22259     fixKeys : function(){ // load time branching for fastest keydown performance
22260         if(Roo.isIE){
22261             return function(e){
22262                 var k = e.getKey(), r;
22263                 if(k == e.TAB){
22264                     e.stopEvent();
22265                     r = this.doc.selection.createRange();
22266                     if(r){
22267                         r.collapse(true);
22268                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22269                         this.deferFocus();
22270                     }
22271                     return;
22272                 }
22273                 
22274                 if(k == e.ENTER){
22275                     r = this.doc.selection.createRange();
22276                     if(r){
22277                         var target = r.parentElement();
22278                         if(!target || target.tagName.toLowerCase() != 'li'){
22279                             e.stopEvent();
22280                             r.pasteHTML('<br />');
22281                             r.collapse(false);
22282                             r.select();
22283                         }
22284                     }
22285                 }
22286                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22287                     this.cleanUpPaste.defer(100, this);
22288                     return;
22289                 }
22290                 
22291                 
22292             };
22293         }else if(Roo.isOpera){
22294             return function(e){
22295                 var k = e.getKey();
22296                 if(k == e.TAB){
22297                     e.stopEvent();
22298                     this.win.focus();
22299                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22300                     this.deferFocus();
22301                 }
22302                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22303                     this.cleanUpPaste.defer(100, this);
22304                     return;
22305                 }
22306                 
22307             };
22308         }else if(Roo.isSafari){
22309             return function(e){
22310                 var k = e.getKey();
22311                 
22312                 if(k == e.TAB){
22313                     e.stopEvent();
22314                     this.execCmd('InsertText','\t');
22315                     this.deferFocus();
22316                     return;
22317                 }
22318                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22319                     this.cleanUpPaste.defer(100, this);
22320                     return;
22321                 }
22322                 
22323              };
22324         }
22325     }(),
22326     
22327     getAllAncestors: function()
22328     {
22329         var p = this.getSelectedNode();
22330         var a = [];
22331         if (!p) {
22332             a.push(p); // push blank onto stack..
22333             p = this.getParentElement();
22334         }
22335         
22336         
22337         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22338             a.push(p);
22339             p = p.parentNode;
22340         }
22341         a.push(this.doc.body);
22342         return a;
22343     },
22344     lastSel : false,
22345     lastSelNode : false,
22346     
22347     
22348     getSelection : function() 
22349     {
22350         this.assignDocWin();
22351         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22352     },
22353     
22354     getSelectedNode: function() 
22355     {
22356         // this may only work on Gecko!!!
22357         
22358         // should we cache this!!!!
22359         
22360         
22361         
22362          
22363         var range = this.createRange(this.getSelection()).cloneRange();
22364         
22365         if (Roo.isIE) {
22366             var parent = range.parentElement();
22367             while (true) {
22368                 var testRange = range.duplicate();
22369                 testRange.moveToElementText(parent);
22370                 if (testRange.inRange(range)) {
22371                     break;
22372                 }
22373                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22374                     break;
22375                 }
22376                 parent = parent.parentElement;
22377             }
22378             return parent;
22379         }
22380         
22381         // is ancestor a text element.
22382         var ac =  range.commonAncestorContainer;
22383         if (ac.nodeType == 3) {
22384             ac = ac.parentNode;
22385         }
22386         
22387         var ar = ac.childNodes;
22388          
22389         var nodes = [];
22390         var other_nodes = [];
22391         var has_other_nodes = false;
22392         for (var i=0;i<ar.length;i++) {
22393             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22394                 continue;
22395             }
22396             // fullly contained node.
22397             
22398             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22399                 nodes.push(ar[i]);
22400                 continue;
22401             }
22402             
22403             // probably selected..
22404             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22405                 other_nodes.push(ar[i]);
22406                 continue;
22407             }
22408             // outer..
22409             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22410                 continue;
22411             }
22412             
22413             
22414             has_other_nodes = true;
22415         }
22416         if (!nodes.length && other_nodes.length) {
22417             nodes= other_nodes;
22418         }
22419         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22420             return false;
22421         }
22422         
22423         return nodes[0];
22424     },
22425     createRange: function(sel)
22426     {
22427         // this has strange effects when using with 
22428         // top toolbar - not sure if it's a great idea.
22429         //this.editor.contentWindow.focus();
22430         if (typeof sel != "undefined") {
22431             try {
22432                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22433             } catch(e) {
22434                 return this.doc.createRange();
22435             }
22436         } else {
22437             return this.doc.createRange();
22438         }
22439     },
22440     getParentElement: function()
22441     {
22442         
22443         this.assignDocWin();
22444         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22445         
22446         var range = this.createRange(sel);
22447          
22448         try {
22449             var p = range.commonAncestorContainer;
22450             while (p.nodeType == 3) { // text node
22451                 p = p.parentNode;
22452             }
22453             return p;
22454         } catch (e) {
22455             return null;
22456         }
22457     
22458     },
22459     /***
22460      *
22461      * Range intersection.. the hard stuff...
22462      *  '-1' = before
22463      *  '0' = hits..
22464      *  '1' = after.
22465      *         [ -- selected range --- ]
22466      *   [fail]                        [fail]
22467      *
22468      *    basically..
22469      *      if end is before start or  hits it. fail.
22470      *      if start is after end or hits it fail.
22471      *
22472      *   if either hits (but other is outside. - then it's not 
22473      *   
22474      *    
22475      **/
22476     
22477     
22478     // @see http://www.thismuchiknow.co.uk/?p=64.
22479     rangeIntersectsNode : function(range, node)
22480     {
22481         var nodeRange = node.ownerDocument.createRange();
22482         try {
22483             nodeRange.selectNode(node);
22484         } catch (e) {
22485             nodeRange.selectNodeContents(node);
22486         }
22487     
22488         var rangeStartRange = range.cloneRange();
22489         rangeStartRange.collapse(true);
22490     
22491         var rangeEndRange = range.cloneRange();
22492         rangeEndRange.collapse(false);
22493     
22494         var nodeStartRange = nodeRange.cloneRange();
22495         nodeStartRange.collapse(true);
22496     
22497         var nodeEndRange = nodeRange.cloneRange();
22498         nodeEndRange.collapse(false);
22499     
22500         return rangeStartRange.compareBoundaryPoints(
22501                  Range.START_TO_START, nodeEndRange) == -1 &&
22502                rangeEndRange.compareBoundaryPoints(
22503                  Range.START_TO_START, nodeStartRange) == 1;
22504         
22505          
22506     },
22507     rangeCompareNode : function(range, node)
22508     {
22509         var nodeRange = node.ownerDocument.createRange();
22510         try {
22511             nodeRange.selectNode(node);
22512         } catch (e) {
22513             nodeRange.selectNodeContents(node);
22514         }
22515         
22516         
22517         range.collapse(true);
22518     
22519         nodeRange.collapse(true);
22520      
22521         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22522         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22523          
22524         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22525         
22526         var nodeIsBefore   =  ss == 1;
22527         var nodeIsAfter    = ee == -1;
22528         
22529         if (nodeIsBefore && nodeIsAfter) {
22530             return 0; // outer
22531         }
22532         if (!nodeIsBefore && nodeIsAfter) {
22533             return 1; //right trailed.
22534         }
22535         
22536         if (nodeIsBefore && !nodeIsAfter) {
22537             return 2;  // left trailed.
22538         }
22539         // fully contined.
22540         return 3;
22541     },
22542
22543     // private? - in a new class?
22544     cleanUpPaste :  function()
22545     {
22546         // cleans up the whole document..
22547         Roo.log('cleanuppaste');
22548         
22549         this.cleanUpChildren(this.doc.body);
22550         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22551         if (clean != this.doc.body.innerHTML) {
22552             this.doc.body.innerHTML = clean;
22553         }
22554         
22555     },
22556     
22557     cleanWordChars : function(input) {// change the chars to hex code
22558         var he = Roo.HtmlEditorCore;
22559         
22560         var output = input;
22561         Roo.each(he.swapCodes, function(sw) { 
22562             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22563             
22564             output = output.replace(swapper, sw[1]);
22565         });
22566         
22567         return output;
22568     },
22569     
22570     
22571     cleanUpChildren : function (n)
22572     {
22573         if (!n.childNodes.length) {
22574             return;
22575         }
22576         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22577            this.cleanUpChild(n.childNodes[i]);
22578         }
22579     },
22580     
22581     
22582         
22583     
22584     cleanUpChild : function (node)
22585     {
22586         var ed = this;
22587         //console.log(node);
22588         if (node.nodeName == "#text") {
22589             // clean up silly Windows -- stuff?
22590             return; 
22591         }
22592         if (node.nodeName == "#comment") {
22593             node.parentNode.removeChild(node);
22594             // clean up silly Windows -- stuff?
22595             return; 
22596         }
22597         var lcname = node.tagName.toLowerCase();
22598         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22599         // whitelist of tags..
22600         
22601         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22602             // remove node.
22603             node.parentNode.removeChild(node);
22604             return;
22605             
22606         }
22607         
22608         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22609         
22610         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22611         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22612         
22613         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22614         //    remove_keep_children = true;
22615         //}
22616         
22617         if (remove_keep_children) {
22618             this.cleanUpChildren(node);
22619             // inserts everything just before this node...
22620             while (node.childNodes.length) {
22621                 var cn = node.childNodes[0];
22622                 node.removeChild(cn);
22623                 node.parentNode.insertBefore(cn, node);
22624             }
22625             node.parentNode.removeChild(node);
22626             return;
22627         }
22628         
22629         if (!node.attributes || !node.attributes.length) {
22630             this.cleanUpChildren(node);
22631             return;
22632         }
22633         
22634         function cleanAttr(n,v)
22635         {
22636             
22637             if (v.match(/^\./) || v.match(/^\//)) {
22638                 return;
22639             }
22640             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22641                 return;
22642             }
22643             if (v.match(/^#/)) {
22644                 return;
22645             }
22646 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22647             node.removeAttribute(n);
22648             
22649         }
22650         
22651         var cwhite = this.cwhite;
22652         var cblack = this.cblack;
22653             
22654         function cleanStyle(n,v)
22655         {
22656             if (v.match(/expression/)) { //XSS?? should we even bother..
22657                 node.removeAttribute(n);
22658                 return;
22659             }
22660             
22661             var parts = v.split(/;/);
22662             var clean = [];
22663             
22664             Roo.each(parts, function(p) {
22665                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22666                 if (!p.length) {
22667                     return true;
22668                 }
22669                 var l = p.split(':').shift().replace(/\s+/g,'');
22670                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22671                 
22672                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22673 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22674                     //node.removeAttribute(n);
22675                     return true;
22676                 }
22677                 //Roo.log()
22678                 // only allow 'c whitelisted system attributes'
22679                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22680 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22681                     //node.removeAttribute(n);
22682                     return true;
22683                 }
22684                 
22685                 
22686                  
22687                 
22688                 clean.push(p);
22689                 return true;
22690             });
22691             if (clean.length) { 
22692                 node.setAttribute(n, clean.join(';'));
22693             } else {
22694                 node.removeAttribute(n);
22695             }
22696             
22697         }
22698         
22699         
22700         for (var i = node.attributes.length-1; i > -1 ; i--) {
22701             var a = node.attributes[i];
22702             //console.log(a);
22703             
22704             if (a.name.toLowerCase().substr(0,2)=='on')  {
22705                 node.removeAttribute(a.name);
22706                 continue;
22707             }
22708             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22709                 node.removeAttribute(a.name);
22710                 continue;
22711             }
22712             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22713                 cleanAttr(a.name,a.value); // fixme..
22714                 continue;
22715             }
22716             if (a.name == 'style') {
22717                 cleanStyle(a.name,a.value);
22718                 continue;
22719             }
22720             /// clean up MS crap..
22721             // tecnically this should be a list of valid class'es..
22722             
22723             
22724             if (a.name == 'class') {
22725                 if (a.value.match(/^Mso/)) {
22726                     node.className = '';
22727                 }
22728                 
22729                 if (a.value.match(/^body$/)) {
22730                     node.className = '';
22731                 }
22732                 continue;
22733             }
22734             
22735             // style cleanup!?
22736             // class cleanup?
22737             
22738         }
22739         
22740         
22741         this.cleanUpChildren(node);
22742         
22743         
22744     },
22745     
22746     /**
22747      * Clean up MS wordisms...
22748      */
22749     cleanWord : function(node)
22750     {
22751         
22752         
22753         if (!node) {
22754             this.cleanWord(this.doc.body);
22755             return;
22756         }
22757         if (node.nodeName == "#text") {
22758             // clean up silly Windows -- stuff?
22759             return; 
22760         }
22761         if (node.nodeName == "#comment") {
22762             node.parentNode.removeChild(node);
22763             // clean up silly Windows -- stuff?
22764             return; 
22765         }
22766         
22767         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22768             node.parentNode.removeChild(node);
22769             return;
22770         }
22771         
22772         // remove - but keep children..
22773         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22774             while (node.childNodes.length) {
22775                 var cn = node.childNodes[0];
22776                 node.removeChild(cn);
22777                 node.parentNode.insertBefore(cn, node);
22778             }
22779             node.parentNode.removeChild(node);
22780             this.iterateChildren(node, this.cleanWord);
22781             return;
22782         }
22783         // clean styles
22784         if (node.className.length) {
22785             
22786             var cn = node.className.split(/\W+/);
22787             var cna = [];
22788             Roo.each(cn, function(cls) {
22789                 if (cls.match(/Mso[a-zA-Z]+/)) {
22790                     return;
22791                 }
22792                 cna.push(cls);
22793             });
22794             node.className = cna.length ? cna.join(' ') : '';
22795             if (!cna.length) {
22796                 node.removeAttribute("class");
22797             }
22798         }
22799         
22800         if (node.hasAttribute("lang")) {
22801             node.removeAttribute("lang");
22802         }
22803         
22804         if (node.hasAttribute("style")) {
22805             
22806             var styles = node.getAttribute("style").split(";");
22807             var nstyle = [];
22808             Roo.each(styles, function(s) {
22809                 if (!s.match(/:/)) {
22810                     return;
22811                 }
22812                 var kv = s.split(":");
22813                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22814                     return;
22815                 }
22816                 // what ever is left... we allow.
22817                 nstyle.push(s);
22818             });
22819             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22820             if (!nstyle.length) {
22821                 node.removeAttribute('style');
22822             }
22823         }
22824         this.iterateChildren(node, this.cleanWord);
22825         
22826         
22827         
22828     },
22829     /**
22830      * iterateChildren of a Node, calling fn each time, using this as the scole..
22831      * @param {DomNode} node node to iterate children of.
22832      * @param {Function} fn method of this class to call on each item.
22833      */
22834     iterateChildren : function(node, fn)
22835     {
22836         if (!node.childNodes.length) {
22837                 return;
22838         }
22839         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22840            fn.call(this, node.childNodes[i])
22841         }
22842     },
22843     
22844     
22845     /**
22846      * cleanTableWidths.
22847      *
22848      * Quite often pasting from word etc.. results in tables with column and widths.
22849      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22850      *
22851      */
22852     cleanTableWidths : function(node)
22853     {
22854          
22855          
22856         if (!node) {
22857             this.cleanTableWidths(this.doc.body);
22858             return;
22859         }
22860         
22861         // ignore list...
22862         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22863             return; 
22864         }
22865         Roo.log(node.tagName);
22866         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22867             this.iterateChildren(node, this.cleanTableWidths);
22868             return;
22869         }
22870         if (node.hasAttribute('width')) {
22871             node.removeAttribute('width');
22872         }
22873         
22874          
22875         if (node.hasAttribute("style")) {
22876             // pretty basic...
22877             
22878             var styles = node.getAttribute("style").split(";");
22879             var nstyle = [];
22880             Roo.each(styles, function(s) {
22881                 if (!s.match(/:/)) {
22882                     return;
22883                 }
22884                 var kv = s.split(":");
22885                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22886                     return;
22887                 }
22888                 // what ever is left... we allow.
22889                 nstyle.push(s);
22890             });
22891             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22892             if (!nstyle.length) {
22893                 node.removeAttribute('style');
22894             }
22895         }
22896         
22897         this.iterateChildren(node, this.cleanTableWidths);
22898         
22899         
22900     },
22901     
22902     
22903     
22904     
22905     domToHTML : function(currentElement, depth, nopadtext) {
22906         
22907         depth = depth || 0;
22908         nopadtext = nopadtext || false;
22909     
22910         if (!currentElement) {
22911             return this.domToHTML(this.doc.body);
22912         }
22913         
22914         //Roo.log(currentElement);
22915         var j;
22916         var allText = false;
22917         var nodeName = currentElement.nodeName;
22918         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22919         
22920         if  (nodeName == '#text') {
22921             
22922             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22923         }
22924         
22925         
22926         var ret = '';
22927         if (nodeName != 'BODY') {
22928              
22929             var i = 0;
22930             // Prints the node tagName, such as <A>, <IMG>, etc
22931             if (tagName) {
22932                 var attr = [];
22933                 for(i = 0; i < currentElement.attributes.length;i++) {
22934                     // quoting?
22935                     var aname = currentElement.attributes.item(i).name;
22936                     if (!currentElement.attributes.item(i).value.length) {
22937                         continue;
22938                     }
22939                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22940                 }
22941                 
22942                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22943             } 
22944             else {
22945                 
22946                 // eack
22947             }
22948         } else {
22949             tagName = false;
22950         }
22951         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22952             return ret;
22953         }
22954         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22955             nopadtext = true;
22956         }
22957         
22958         
22959         // Traverse the tree
22960         i = 0;
22961         var currentElementChild = currentElement.childNodes.item(i);
22962         var allText = true;
22963         var innerHTML  = '';
22964         lastnode = '';
22965         while (currentElementChild) {
22966             // Formatting code (indent the tree so it looks nice on the screen)
22967             var nopad = nopadtext;
22968             if (lastnode == 'SPAN') {
22969                 nopad  = true;
22970             }
22971             // text
22972             if  (currentElementChild.nodeName == '#text') {
22973                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22974                 toadd = nopadtext ? toadd : toadd.trim();
22975                 if (!nopad && toadd.length > 80) {
22976                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22977                 }
22978                 innerHTML  += toadd;
22979                 
22980                 i++;
22981                 currentElementChild = currentElement.childNodes.item(i);
22982                 lastNode = '';
22983                 continue;
22984             }
22985             allText = false;
22986             
22987             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22988                 
22989             // Recursively traverse the tree structure of the child node
22990             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22991             lastnode = currentElementChild.nodeName;
22992             i++;
22993             currentElementChild=currentElement.childNodes.item(i);
22994         }
22995         
22996         ret += innerHTML;
22997         
22998         if (!allText) {
22999                 // The remaining code is mostly for formatting the tree
23000             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23001         }
23002         
23003         
23004         if (tagName) {
23005             ret+= "</"+tagName+">";
23006         }
23007         return ret;
23008         
23009     },
23010         
23011     applyBlacklists : function()
23012     {
23013         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23014         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23015         
23016         this.white = [];
23017         this.black = [];
23018         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23019             if (b.indexOf(tag) > -1) {
23020                 return;
23021             }
23022             this.white.push(tag);
23023             
23024         }, this);
23025         
23026         Roo.each(w, function(tag) {
23027             if (b.indexOf(tag) > -1) {
23028                 return;
23029             }
23030             if (this.white.indexOf(tag) > -1) {
23031                 return;
23032             }
23033             this.white.push(tag);
23034             
23035         }, this);
23036         
23037         
23038         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23039             if (w.indexOf(tag) > -1) {
23040                 return;
23041             }
23042             this.black.push(tag);
23043             
23044         }, this);
23045         
23046         Roo.each(b, function(tag) {
23047             if (w.indexOf(tag) > -1) {
23048                 return;
23049             }
23050             if (this.black.indexOf(tag) > -1) {
23051                 return;
23052             }
23053             this.black.push(tag);
23054             
23055         }, this);
23056         
23057         
23058         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23059         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23060         
23061         this.cwhite = [];
23062         this.cblack = [];
23063         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23064             if (b.indexOf(tag) > -1) {
23065                 return;
23066             }
23067             this.cwhite.push(tag);
23068             
23069         }, this);
23070         
23071         Roo.each(w, function(tag) {
23072             if (b.indexOf(tag) > -1) {
23073                 return;
23074             }
23075             if (this.cwhite.indexOf(tag) > -1) {
23076                 return;
23077             }
23078             this.cwhite.push(tag);
23079             
23080         }, this);
23081         
23082         
23083         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23084             if (w.indexOf(tag) > -1) {
23085                 return;
23086             }
23087             this.cblack.push(tag);
23088             
23089         }, this);
23090         
23091         Roo.each(b, function(tag) {
23092             if (w.indexOf(tag) > -1) {
23093                 return;
23094             }
23095             if (this.cblack.indexOf(tag) > -1) {
23096                 return;
23097             }
23098             this.cblack.push(tag);
23099             
23100         }, this);
23101     },
23102     
23103     setStylesheets : function(stylesheets)
23104     {
23105         if(typeof(stylesheets) == 'string'){
23106             Roo.get(this.iframe.contentDocument.head).createChild({
23107                 tag : 'link',
23108                 rel : 'stylesheet',
23109                 type : 'text/css',
23110                 href : stylesheets
23111             });
23112             
23113             return;
23114         }
23115         var _this = this;
23116      
23117         Roo.each(stylesheets, function(s) {
23118             if(!s.length){
23119                 return;
23120             }
23121             
23122             Roo.get(_this.iframe.contentDocument.head).createChild({
23123                 tag : 'link',
23124                 rel : 'stylesheet',
23125                 type : 'text/css',
23126                 href : s
23127             });
23128         });
23129
23130         
23131     },
23132     
23133     removeStylesheets : function()
23134     {
23135         var _this = this;
23136         
23137         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23138             s.remove();
23139         });
23140     },
23141     
23142     setStyle : function(style)
23143     {
23144         Roo.get(this.iframe.contentDocument.head).createChild({
23145             tag : 'style',
23146             type : 'text/css',
23147             html : style
23148         });
23149
23150         return;
23151     }
23152     
23153     // hide stuff that is not compatible
23154     /**
23155      * @event blur
23156      * @hide
23157      */
23158     /**
23159      * @event change
23160      * @hide
23161      */
23162     /**
23163      * @event focus
23164      * @hide
23165      */
23166     /**
23167      * @event specialkey
23168      * @hide
23169      */
23170     /**
23171      * @cfg {String} fieldClass @hide
23172      */
23173     /**
23174      * @cfg {String} focusClass @hide
23175      */
23176     /**
23177      * @cfg {String} autoCreate @hide
23178      */
23179     /**
23180      * @cfg {String} inputType @hide
23181      */
23182     /**
23183      * @cfg {String} invalidClass @hide
23184      */
23185     /**
23186      * @cfg {String} invalidText @hide
23187      */
23188     /**
23189      * @cfg {String} msgFx @hide
23190      */
23191     /**
23192      * @cfg {String} validateOnBlur @hide
23193      */
23194 });
23195
23196 Roo.HtmlEditorCore.white = [
23197         'area', 'br', 'img', 'input', 'hr', 'wbr',
23198         
23199        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23200        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23201        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23202        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23203        'table',   'ul',         'xmp', 
23204        
23205        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23206       'thead',   'tr', 
23207      
23208       'dir', 'menu', 'ol', 'ul', 'dl',
23209        
23210       'embed',  'object'
23211 ];
23212
23213
23214 Roo.HtmlEditorCore.black = [
23215     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23216         'applet', // 
23217         'base',   'basefont', 'bgsound', 'blink',  'body', 
23218         'frame',  'frameset', 'head',    'html',   'ilayer', 
23219         'iframe', 'layer',  'link',     'meta',    'object',   
23220         'script', 'style' ,'title',  'xml' // clean later..
23221 ];
23222 Roo.HtmlEditorCore.clean = [
23223     'script', 'style', 'title', 'xml'
23224 ];
23225 Roo.HtmlEditorCore.remove = [
23226     'font'
23227 ];
23228 // attributes..
23229
23230 Roo.HtmlEditorCore.ablack = [
23231     'on'
23232 ];
23233     
23234 Roo.HtmlEditorCore.aclean = [ 
23235     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23236 ];
23237
23238 // protocols..
23239 Roo.HtmlEditorCore.pwhite= [
23240         'http',  'https',  'mailto'
23241 ];
23242
23243 // white listed style attributes.
23244 Roo.HtmlEditorCore.cwhite= [
23245       //  'text-align', /// default is to allow most things..
23246       
23247          
23248 //        'font-size'//??
23249 ];
23250
23251 // black listed style attributes.
23252 Roo.HtmlEditorCore.cblack= [
23253       //  'font-size' -- this can be set by the project 
23254 ];
23255
23256
23257 Roo.HtmlEditorCore.swapCodes   =[ 
23258     [    8211, "--" ], 
23259     [    8212, "--" ], 
23260     [    8216,  "'" ],  
23261     [    8217, "'" ],  
23262     [    8220, '"' ],  
23263     [    8221, '"' ],  
23264     [    8226, "*" ],  
23265     [    8230, "..." ]
23266 ]; 
23267
23268     /*
23269  * - LGPL
23270  *
23271  * HtmlEditor
23272  * 
23273  */
23274
23275 /**
23276  * @class Roo.bootstrap.HtmlEditor
23277  * @extends Roo.bootstrap.TextArea
23278  * Bootstrap HtmlEditor class
23279
23280  * @constructor
23281  * Create a new HtmlEditor
23282  * @param {Object} config The config object
23283  */
23284
23285 Roo.bootstrap.HtmlEditor = function(config){
23286     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23287     if (!this.toolbars) {
23288         this.toolbars = [];
23289     }
23290     
23291     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23292     this.addEvents({
23293             /**
23294              * @event initialize
23295              * Fires when the editor is fully initialized (including the iframe)
23296              * @param {HtmlEditor} this
23297              */
23298             initialize: true,
23299             /**
23300              * @event activate
23301              * Fires when the editor is first receives the focus. Any insertion must wait
23302              * until after this event.
23303              * @param {HtmlEditor} this
23304              */
23305             activate: true,
23306              /**
23307              * @event beforesync
23308              * Fires before the textarea is updated with content from the editor iframe. Return false
23309              * to cancel the sync.
23310              * @param {HtmlEditor} this
23311              * @param {String} html
23312              */
23313             beforesync: true,
23314              /**
23315              * @event beforepush
23316              * Fires before the iframe editor is updated with content from the textarea. Return false
23317              * to cancel the push.
23318              * @param {HtmlEditor} this
23319              * @param {String} html
23320              */
23321             beforepush: true,
23322              /**
23323              * @event sync
23324              * Fires when the textarea is updated with content from the editor iframe.
23325              * @param {HtmlEditor} this
23326              * @param {String} html
23327              */
23328             sync: true,
23329              /**
23330              * @event push
23331              * Fires when the iframe editor is updated with content from the textarea.
23332              * @param {HtmlEditor} this
23333              * @param {String} html
23334              */
23335             push: true,
23336              /**
23337              * @event editmodechange
23338              * Fires when the editor switches edit modes
23339              * @param {HtmlEditor} this
23340              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23341              */
23342             editmodechange: true,
23343             /**
23344              * @event editorevent
23345              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23346              * @param {HtmlEditor} this
23347              */
23348             editorevent: true,
23349             /**
23350              * @event firstfocus
23351              * Fires when on first focus - needed by toolbars..
23352              * @param {HtmlEditor} this
23353              */
23354             firstfocus: true,
23355             /**
23356              * @event autosave
23357              * Auto save the htmlEditor value as a file into Events
23358              * @param {HtmlEditor} this
23359              */
23360             autosave: true,
23361             /**
23362              * @event savedpreview
23363              * preview the saved version of htmlEditor
23364              * @param {HtmlEditor} this
23365              */
23366             savedpreview: true
23367         });
23368 };
23369
23370
23371 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23372     
23373     
23374       /**
23375      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23376      */
23377     toolbars : false,
23378     
23379      /**
23380     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23381     */
23382     btns : [],
23383    
23384      /**
23385      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23386      *                        Roo.resizable.
23387      */
23388     resizable : false,
23389      /**
23390      * @cfg {Number} height (in pixels)
23391      */   
23392     height: 300,
23393    /**
23394      * @cfg {Number} width (in pixels)
23395      */   
23396     width: false,
23397     
23398     /**
23399      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23400      * 
23401      */
23402     stylesheets: false,
23403     
23404     // id of frame..
23405     frameId: false,
23406     
23407     // private properties
23408     validationEvent : false,
23409     deferHeight: true,
23410     initialized : false,
23411     activated : false,
23412     
23413     onFocus : Roo.emptyFn,
23414     iframePad:3,
23415     hideMode:'offsets',
23416     
23417     tbContainer : false,
23418     
23419     bodyCls : '',
23420     
23421     toolbarContainer :function() {
23422         return this.wrap.select('.x-html-editor-tb',true).first();
23423     },
23424
23425     /**
23426      * Protected method that will not generally be called directly. It
23427      * is called when the editor creates its toolbar. Override this method if you need to
23428      * add custom toolbar buttons.
23429      * @param {HtmlEditor} editor
23430      */
23431     createToolbar : function(){
23432         Roo.log('renewing');
23433         Roo.log("create toolbars");
23434         
23435         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23436         this.toolbars[0].render(this.toolbarContainer());
23437         
23438         return;
23439         
23440 //        if (!editor.toolbars || !editor.toolbars.length) {
23441 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23442 //        }
23443 //        
23444 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23445 //            editor.toolbars[i] = Roo.factory(
23446 //                    typeof(editor.toolbars[i]) == 'string' ?
23447 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23448 //                Roo.bootstrap.HtmlEditor);
23449 //            editor.toolbars[i].init(editor);
23450 //        }
23451     },
23452
23453      
23454     // private
23455     onRender : function(ct, position)
23456     {
23457        // Roo.log("Call onRender: " + this.xtype);
23458         var _t = this;
23459         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23460       
23461         this.wrap = this.inputEl().wrap({
23462             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23463         });
23464         
23465         this.editorcore.onRender(ct, position);
23466          
23467         if (this.resizable) {
23468             this.resizeEl = new Roo.Resizable(this.wrap, {
23469                 pinned : true,
23470                 wrap: true,
23471                 dynamic : true,
23472                 minHeight : this.height,
23473                 height: this.height,
23474                 handles : this.resizable,
23475                 width: this.width,
23476                 listeners : {
23477                     resize : function(r, w, h) {
23478                         _t.onResize(w,h); // -something
23479                     }
23480                 }
23481             });
23482             
23483         }
23484         this.createToolbar(this);
23485        
23486         
23487         if(!this.width && this.resizable){
23488             this.setSize(this.wrap.getSize());
23489         }
23490         if (this.resizeEl) {
23491             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23492             // should trigger onReize..
23493         }
23494         
23495     },
23496
23497     // private
23498     onResize : function(w, h)
23499     {
23500         Roo.log('resize: ' +w + ',' + h );
23501         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23502         var ew = false;
23503         var eh = false;
23504         
23505         if(this.inputEl() ){
23506             if(typeof w == 'number'){
23507                 var aw = w - this.wrap.getFrameWidth('lr');
23508                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23509                 ew = aw;
23510             }
23511             if(typeof h == 'number'){
23512                  var tbh = -11;  // fixme it needs to tool bar size!
23513                 for (var i =0; i < this.toolbars.length;i++) {
23514                     // fixme - ask toolbars for heights?
23515                     tbh += this.toolbars[i].el.getHeight();
23516                     //if (this.toolbars[i].footer) {
23517                     //    tbh += this.toolbars[i].footer.el.getHeight();
23518                     //}
23519                 }
23520               
23521                 
23522                 
23523                 
23524                 
23525                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23526                 ah -= 5; // knock a few pixes off for look..
23527                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23528                 var eh = ah;
23529             }
23530         }
23531         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23532         this.editorcore.onResize(ew,eh);
23533         
23534     },
23535
23536     /**
23537      * Toggles the editor between standard and source edit mode.
23538      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23539      */
23540     toggleSourceEdit : function(sourceEditMode)
23541     {
23542         this.editorcore.toggleSourceEdit(sourceEditMode);
23543         
23544         if(this.editorcore.sourceEditMode){
23545             Roo.log('editor - showing textarea');
23546             
23547 //            Roo.log('in');
23548 //            Roo.log(this.syncValue());
23549             this.syncValue();
23550             this.inputEl().removeClass(['hide', 'x-hidden']);
23551             this.inputEl().dom.removeAttribute('tabIndex');
23552             this.inputEl().focus();
23553         }else{
23554             Roo.log('editor - hiding textarea');
23555 //            Roo.log('out')
23556 //            Roo.log(this.pushValue()); 
23557             this.pushValue();
23558             
23559             this.inputEl().addClass(['hide', 'x-hidden']);
23560             this.inputEl().dom.setAttribute('tabIndex', -1);
23561             //this.deferFocus();
23562         }
23563          
23564         if(this.resizable){
23565             this.setSize(this.wrap.getSize());
23566         }
23567         
23568         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23569     },
23570  
23571     // private (for BoxComponent)
23572     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23573
23574     // private (for BoxComponent)
23575     getResizeEl : function(){
23576         return this.wrap;
23577     },
23578
23579     // private (for BoxComponent)
23580     getPositionEl : function(){
23581         return this.wrap;
23582     },
23583
23584     // private
23585     initEvents : function(){
23586         this.originalValue = this.getValue();
23587     },
23588
23589 //    /**
23590 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23591 //     * @method
23592 //     */
23593 //    markInvalid : Roo.emptyFn,
23594 //    /**
23595 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23596 //     * @method
23597 //     */
23598 //    clearInvalid : Roo.emptyFn,
23599
23600     setValue : function(v){
23601         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23602         this.editorcore.pushValue();
23603     },
23604
23605      
23606     // private
23607     deferFocus : function(){
23608         this.focus.defer(10, this);
23609     },
23610
23611     // doc'ed in Field
23612     focus : function(){
23613         this.editorcore.focus();
23614         
23615     },
23616       
23617
23618     // private
23619     onDestroy : function(){
23620         
23621         
23622         
23623         if(this.rendered){
23624             
23625             for (var i =0; i < this.toolbars.length;i++) {
23626                 // fixme - ask toolbars for heights?
23627                 this.toolbars[i].onDestroy();
23628             }
23629             
23630             this.wrap.dom.innerHTML = '';
23631             this.wrap.remove();
23632         }
23633     },
23634
23635     // private
23636     onFirstFocus : function(){
23637         //Roo.log("onFirstFocus");
23638         this.editorcore.onFirstFocus();
23639          for (var i =0; i < this.toolbars.length;i++) {
23640             this.toolbars[i].onFirstFocus();
23641         }
23642         
23643     },
23644     
23645     // private
23646     syncValue : function()
23647     {   
23648         this.editorcore.syncValue();
23649     },
23650     
23651     pushValue : function()
23652     {   
23653         this.editorcore.pushValue();
23654     }
23655      
23656     
23657     // hide stuff that is not compatible
23658     /**
23659      * @event blur
23660      * @hide
23661      */
23662     /**
23663      * @event change
23664      * @hide
23665      */
23666     /**
23667      * @event focus
23668      * @hide
23669      */
23670     /**
23671      * @event specialkey
23672      * @hide
23673      */
23674     /**
23675      * @cfg {String} fieldClass @hide
23676      */
23677     /**
23678      * @cfg {String} focusClass @hide
23679      */
23680     /**
23681      * @cfg {String} autoCreate @hide
23682      */
23683     /**
23684      * @cfg {String} inputType @hide
23685      */
23686     /**
23687      * @cfg {String} invalidClass @hide
23688      */
23689     /**
23690      * @cfg {String} invalidText @hide
23691      */
23692     /**
23693      * @cfg {String} msgFx @hide
23694      */
23695     /**
23696      * @cfg {String} validateOnBlur @hide
23697      */
23698 });
23699  
23700     
23701    
23702    
23703    
23704       
23705 Roo.namespace('Roo.bootstrap.htmleditor');
23706 /**
23707  * @class Roo.bootstrap.HtmlEditorToolbar1
23708  * Basic Toolbar
23709  * 
23710  * Usage:
23711  *
23712  new Roo.bootstrap.HtmlEditor({
23713     ....
23714     toolbars : [
23715         new Roo.bootstrap.HtmlEditorToolbar1({
23716             disable : { fonts: 1 , format: 1, ..., ... , ...],
23717             btns : [ .... ]
23718         })
23719     }
23720      
23721  * 
23722  * @cfg {Object} disable List of elements to disable..
23723  * @cfg {Array} btns List of additional buttons.
23724  * 
23725  * 
23726  * NEEDS Extra CSS? 
23727  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23728  */
23729  
23730 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23731 {
23732     
23733     Roo.apply(this, config);
23734     
23735     // default disabled, based on 'good practice'..
23736     this.disable = this.disable || {};
23737     Roo.applyIf(this.disable, {
23738         fontSize : true,
23739         colors : true,
23740         specialElements : true
23741     });
23742     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23743     
23744     this.editor = config.editor;
23745     this.editorcore = config.editor.editorcore;
23746     
23747     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23748     
23749     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23750     // dont call parent... till later.
23751 }
23752 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23753      
23754     bar : true,
23755     
23756     editor : false,
23757     editorcore : false,
23758     
23759     
23760     formats : [
23761         "p" ,  
23762         "h1","h2","h3","h4","h5","h6", 
23763         "pre", "code", 
23764         "abbr", "acronym", "address", "cite", "samp", "var",
23765         'div','span'
23766     ],
23767     
23768     onRender : function(ct, position)
23769     {
23770        // Roo.log("Call onRender: " + this.xtype);
23771         
23772        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23773        Roo.log(this.el);
23774        this.el.dom.style.marginBottom = '0';
23775        var _this = this;
23776        var editorcore = this.editorcore;
23777        var editor= this.editor;
23778        
23779        var children = [];
23780        var btn = function(id,cmd , toggle, handler, html){
23781        
23782             var  event = toggle ? 'toggle' : 'click';
23783        
23784             var a = {
23785                 size : 'sm',
23786                 xtype: 'Button',
23787                 xns: Roo.bootstrap,
23788                 glyphicon : id,
23789                 cmd : id || cmd,
23790                 enableToggle:toggle !== false,
23791                 html : html || '',
23792                 pressed : toggle ? false : null,
23793                 listeners : {}
23794             };
23795             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23796                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23797             };
23798             children.push(a);
23799             return a;
23800        }
23801        
23802     //    var cb_box = function...
23803         
23804         var style = {
23805                 xtype: 'Button',
23806                 size : 'sm',
23807                 xns: Roo.bootstrap,
23808                 glyphicon : 'font',
23809                 //html : 'submit'
23810                 menu : {
23811                     xtype: 'Menu',
23812                     xns: Roo.bootstrap,
23813                     items:  []
23814                 }
23815         };
23816         Roo.each(this.formats, function(f) {
23817             style.menu.items.push({
23818                 xtype :'MenuItem',
23819                 xns: Roo.bootstrap,
23820                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23821                 tagname : f,
23822                 listeners : {
23823                     click : function()
23824                     {
23825                         editorcore.insertTag(this.tagname);
23826                         editor.focus();
23827                     }
23828                 }
23829                 
23830             });
23831         });
23832         children.push(style);   
23833         
23834         btn('bold',false,true);
23835         btn('italic',false,true);
23836         btn('align-left', 'justifyleft',true);
23837         btn('align-center', 'justifycenter',true);
23838         btn('align-right' , 'justifyright',true);
23839         btn('link', false, false, function(btn) {
23840             //Roo.log("create link?");
23841             var url = prompt(this.createLinkText, this.defaultLinkValue);
23842             if(url && url != 'http:/'+'/'){
23843                 this.editorcore.relayCmd('createlink', url);
23844             }
23845         }),
23846         btn('list','insertunorderedlist',true);
23847         btn('pencil', false,true, function(btn){
23848                 Roo.log(this);
23849                 this.toggleSourceEdit(btn.pressed);
23850         });
23851         
23852         if (this.editor.btns.length > 0) {
23853             for (var i = 0; i<this.editor.btns.length; i++) {
23854                 children.push(this.editor.btns[i]);
23855             }
23856         }
23857         
23858         /*
23859         var cog = {
23860                 xtype: 'Button',
23861                 size : 'sm',
23862                 xns: Roo.bootstrap,
23863                 glyphicon : 'cog',
23864                 //html : 'submit'
23865                 menu : {
23866                     xtype: 'Menu',
23867                     xns: Roo.bootstrap,
23868                     items:  []
23869                 }
23870         };
23871         
23872         cog.menu.items.push({
23873             xtype :'MenuItem',
23874             xns: Roo.bootstrap,
23875             html : Clean styles,
23876             tagname : f,
23877             listeners : {
23878                 click : function()
23879                 {
23880                     editorcore.insertTag(this.tagname);
23881                     editor.focus();
23882                 }
23883             }
23884             
23885         });
23886        */
23887         
23888          
23889        this.xtype = 'NavSimplebar';
23890         
23891         for(var i=0;i< children.length;i++) {
23892             
23893             this.buttons.add(this.addxtypeChild(children[i]));
23894             
23895         }
23896         
23897         editor.on('editorevent', this.updateToolbar, this);
23898     },
23899     onBtnClick : function(id)
23900     {
23901        this.editorcore.relayCmd(id);
23902        this.editorcore.focus();
23903     },
23904     
23905     /**
23906      * Protected method that will not generally be called directly. It triggers
23907      * a toolbar update by reading the markup state of the current selection in the editor.
23908      */
23909     updateToolbar: function(){
23910
23911         if(!this.editorcore.activated){
23912             this.editor.onFirstFocus(); // is this neeed?
23913             return;
23914         }
23915
23916         var btns = this.buttons; 
23917         var doc = this.editorcore.doc;
23918         btns.get('bold').setActive(doc.queryCommandState('bold'));
23919         btns.get('italic').setActive(doc.queryCommandState('italic'));
23920         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23921         
23922         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23923         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23924         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23925         
23926         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23927         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23928          /*
23929         
23930         var ans = this.editorcore.getAllAncestors();
23931         if (this.formatCombo) {
23932             
23933             
23934             var store = this.formatCombo.store;
23935             this.formatCombo.setValue("");
23936             for (var i =0; i < ans.length;i++) {
23937                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23938                     // select it..
23939                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23940                     break;
23941                 }
23942             }
23943         }
23944         
23945         
23946         
23947         // hides menus... - so this cant be on a menu...
23948         Roo.bootstrap.MenuMgr.hideAll();
23949         */
23950         Roo.bootstrap.MenuMgr.hideAll();
23951         //this.editorsyncValue();
23952     },
23953     onFirstFocus: function() {
23954         this.buttons.each(function(item){
23955            item.enable();
23956         });
23957     },
23958     toggleSourceEdit : function(sourceEditMode){
23959         
23960           
23961         if(sourceEditMode){
23962             Roo.log("disabling buttons");
23963            this.buttons.each( function(item){
23964                 if(item.cmd != 'pencil'){
23965                     item.disable();
23966                 }
23967             });
23968           
23969         }else{
23970             Roo.log("enabling buttons");
23971             if(this.editorcore.initialized){
23972                 this.buttons.each( function(item){
23973                     item.enable();
23974                 });
23975             }
23976             
23977         }
23978         Roo.log("calling toggole on editor");
23979         // tell the editor that it's been pressed..
23980         this.editor.toggleSourceEdit(sourceEditMode);
23981        
23982     }
23983 });
23984
23985
23986
23987
23988
23989 /**
23990  * @class Roo.bootstrap.Table.AbstractSelectionModel
23991  * @extends Roo.util.Observable
23992  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23993  * implemented by descendant classes.  This class should not be directly instantiated.
23994  * @constructor
23995  */
23996 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23997     this.locked = false;
23998     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23999 };
24000
24001
24002 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24003     /** @ignore Called by the grid automatically. Do not call directly. */
24004     init : function(grid){
24005         this.grid = grid;
24006         this.initEvents();
24007     },
24008
24009     /**
24010      * Locks the selections.
24011      */
24012     lock : function(){
24013         this.locked = true;
24014     },
24015
24016     /**
24017      * Unlocks the selections.
24018      */
24019     unlock : function(){
24020         this.locked = false;
24021     },
24022
24023     /**
24024      * Returns true if the selections are locked.
24025      * @return {Boolean}
24026      */
24027     isLocked : function(){
24028         return this.locked;
24029     }
24030 });
24031 /**
24032  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24033  * @class Roo.bootstrap.Table.RowSelectionModel
24034  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24035  * It supports multiple selections and keyboard selection/navigation. 
24036  * @constructor
24037  * @param {Object} config
24038  */
24039
24040 Roo.bootstrap.Table.RowSelectionModel = function(config){
24041     Roo.apply(this, config);
24042     this.selections = new Roo.util.MixedCollection(false, function(o){
24043         return o.id;
24044     });
24045
24046     this.last = false;
24047     this.lastActive = false;
24048
24049     this.addEvents({
24050         /**
24051              * @event selectionchange
24052              * Fires when the selection changes
24053              * @param {SelectionModel} this
24054              */
24055             "selectionchange" : true,
24056         /**
24057              * @event afterselectionchange
24058              * Fires after the selection changes (eg. by key press or clicking)
24059              * @param {SelectionModel} this
24060              */
24061             "afterselectionchange" : true,
24062         /**
24063              * @event beforerowselect
24064              * Fires when a row is selected being selected, return false to cancel.
24065              * @param {SelectionModel} this
24066              * @param {Number} rowIndex The selected index
24067              * @param {Boolean} keepExisting False if other selections will be cleared
24068              */
24069             "beforerowselect" : true,
24070         /**
24071              * @event rowselect
24072              * Fires when a row is selected.
24073              * @param {SelectionModel} this
24074              * @param {Number} rowIndex The selected index
24075              * @param {Roo.data.Record} r The record
24076              */
24077             "rowselect" : true,
24078         /**
24079              * @event rowdeselect
24080              * Fires when a row is deselected.
24081              * @param {SelectionModel} this
24082              * @param {Number} rowIndex The selected index
24083              */
24084         "rowdeselect" : true
24085     });
24086     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24087     this.locked = false;
24088  };
24089
24090 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24091     /**
24092      * @cfg {Boolean} singleSelect
24093      * True to allow selection of only one row at a time (defaults to false)
24094      */
24095     singleSelect : false,
24096
24097     // private
24098     initEvents : function()
24099     {
24100
24101         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24102         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24103         //}else{ // allow click to work like normal
24104          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24105         //}
24106         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24107         this.grid.on("rowclick", this.handleMouseDown, this);
24108         
24109         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24110             "up" : function(e){
24111                 if(!e.shiftKey){
24112                     this.selectPrevious(e.shiftKey);
24113                 }else if(this.last !== false && this.lastActive !== false){
24114                     var last = this.last;
24115                     this.selectRange(this.last,  this.lastActive-1);
24116                     this.grid.getView().focusRow(this.lastActive);
24117                     if(last !== false){
24118                         this.last = last;
24119                     }
24120                 }else{
24121                     this.selectFirstRow();
24122                 }
24123                 this.fireEvent("afterselectionchange", this);
24124             },
24125             "down" : function(e){
24126                 if(!e.shiftKey){
24127                     this.selectNext(e.shiftKey);
24128                 }else if(this.last !== false && this.lastActive !== false){
24129                     var last = this.last;
24130                     this.selectRange(this.last,  this.lastActive+1);
24131                     this.grid.getView().focusRow(this.lastActive);
24132                     if(last !== false){
24133                         this.last = last;
24134                     }
24135                 }else{
24136                     this.selectFirstRow();
24137                 }
24138                 this.fireEvent("afterselectionchange", this);
24139             },
24140             scope: this
24141         });
24142         this.grid.store.on('load', function(){
24143             this.selections.clear();
24144         },this);
24145         /*
24146         var view = this.grid.view;
24147         view.on("refresh", this.onRefresh, this);
24148         view.on("rowupdated", this.onRowUpdated, this);
24149         view.on("rowremoved", this.onRemove, this);
24150         */
24151     },
24152
24153     // private
24154     onRefresh : function()
24155     {
24156         var ds = this.grid.store, i, v = this.grid.view;
24157         var s = this.selections;
24158         s.each(function(r){
24159             if((i = ds.indexOfId(r.id)) != -1){
24160                 v.onRowSelect(i);
24161             }else{
24162                 s.remove(r);
24163             }
24164         });
24165     },
24166
24167     // private
24168     onRemove : function(v, index, r){
24169         this.selections.remove(r);
24170     },
24171
24172     // private
24173     onRowUpdated : function(v, index, r){
24174         if(this.isSelected(r)){
24175             v.onRowSelect(index);
24176         }
24177     },
24178
24179     /**
24180      * Select records.
24181      * @param {Array} records The records to select
24182      * @param {Boolean} keepExisting (optional) True to keep existing selections
24183      */
24184     selectRecords : function(records, keepExisting)
24185     {
24186         if(!keepExisting){
24187             this.clearSelections();
24188         }
24189             var ds = this.grid.store;
24190         for(var i = 0, len = records.length; i < len; i++){
24191             this.selectRow(ds.indexOf(records[i]), true);
24192         }
24193     },
24194
24195     /**
24196      * Gets the number of selected rows.
24197      * @return {Number}
24198      */
24199     getCount : function(){
24200         return this.selections.length;
24201     },
24202
24203     /**
24204      * Selects the first row in the grid.
24205      */
24206     selectFirstRow : function(){
24207         this.selectRow(0);
24208     },
24209
24210     /**
24211      * Select the last row.
24212      * @param {Boolean} keepExisting (optional) True to keep existing selections
24213      */
24214     selectLastRow : function(keepExisting){
24215         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24216         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24217     },
24218
24219     /**
24220      * Selects the row immediately following the last selected row.
24221      * @param {Boolean} keepExisting (optional) True to keep existing selections
24222      */
24223     selectNext : function(keepExisting)
24224     {
24225             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24226             this.selectRow(this.last+1, keepExisting);
24227             this.grid.getView().focusRow(this.last);
24228         }
24229     },
24230
24231     /**
24232      * Selects the row that precedes the last selected row.
24233      * @param {Boolean} keepExisting (optional) True to keep existing selections
24234      */
24235     selectPrevious : function(keepExisting){
24236         if(this.last){
24237             this.selectRow(this.last-1, keepExisting);
24238             this.grid.getView().focusRow(this.last);
24239         }
24240     },
24241
24242     /**
24243      * Returns the selected records
24244      * @return {Array} Array of selected records
24245      */
24246     getSelections : function(){
24247         return [].concat(this.selections.items);
24248     },
24249
24250     /**
24251      * Returns the first selected record.
24252      * @return {Record}
24253      */
24254     getSelected : function(){
24255         return this.selections.itemAt(0);
24256     },
24257
24258
24259     /**
24260      * Clears all selections.
24261      */
24262     clearSelections : function(fast)
24263     {
24264         if(this.locked) {
24265             return;
24266         }
24267         if(fast !== true){
24268                 var ds = this.grid.store;
24269             var s = this.selections;
24270             s.each(function(r){
24271                 this.deselectRow(ds.indexOfId(r.id));
24272             }, this);
24273             s.clear();
24274         }else{
24275             this.selections.clear();
24276         }
24277         this.last = false;
24278     },
24279
24280
24281     /**
24282      * Selects all rows.
24283      */
24284     selectAll : function(){
24285         if(this.locked) {
24286             return;
24287         }
24288         this.selections.clear();
24289         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24290             this.selectRow(i, true);
24291         }
24292     },
24293
24294     /**
24295      * Returns True if there is a selection.
24296      * @return {Boolean}
24297      */
24298     hasSelection : function(){
24299         return this.selections.length > 0;
24300     },
24301
24302     /**
24303      * Returns True if the specified row is selected.
24304      * @param {Number/Record} record The record or index of the record to check
24305      * @return {Boolean}
24306      */
24307     isSelected : function(index){
24308             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24309         return (r && this.selections.key(r.id) ? true : false);
24310     },
24311
24312     /**
24313      * Returns True if the specified record id is selected.
24314      * @param {String} id The id of record to check
24315      * @return {Boolean}
24316      */
24317     isIdSelected : function(id){
24318         return (this.selections.key(id) ? true : false);
24319     },
24320
24321
24322     // private
24323     handleMouseDBClick : function(e, t){
24324         
24325     },
24326     // private
24327     handleMouseDown : function(e, t)
24328     {
24329             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24330         if(this.isLocked() || rowIndex < 0 ){
24331             return;
24332         };
24333         if(e.shiftKey && this.last !== false){
24334             var last = this.last;
24335             this.selectRange(last, rowIndex, e.ctrlKey);
24336             this.last = last; // reset the last
24337             t.focus();
24338     
24339         }else{
24340             var isSelected = this.isSelected(rowIndex);
24341             //Roo.log("select row:" + rowIndex);
24342             if(isSelected){
24343                 this.deselectRow(rowIndex);
24344             } else {
24345                         this.selectRow(rowIndex, true);
24346             }
24347     
24348             /*
24349                 if(e.button !== 0 && isSelected){
24350                 alert('rowIndex 2: ' + rowIndex);
24351                     view.focusRow(rowIndex);
24352                 }else if(e.ctrlKey && isSelected){
24353                     this.deselectRow(rowIndex);
24354                 }else if(!isSelected){
24355                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24356                     view.focusRow(rowIndex);
24357                 }
24358             */
24359         }
24360         this.fireEvent("afterselectionchange", this);
24361     },
24362     // private
24363     handleDragableRowClick :  function(grid, rowIndex, e) 
24364     {
24365         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24366             this.selectRow(rowIndex, false);
24367             grid.view.focusRow(rowIndex);
24368              this.fireEvent("afterselectionchange", this);
24369         }
24370     },
24371     
24372     /**
24373      * Selects multiple rows.
24374      * @param {Array} rows Array of the indexes of the row to select
24375      * @param {Boolean} keepExisting (optional) True to keep existing selections
24376      */
24377     selectRows : function(rows, keepExisting){
24378         if(!keepExisting){
24379             this.clearSelections();
24380         }
24381         for(var i = 0, len = rows.length; i < len; i++){
24382             this.selectRow(rows[i], true);
24383         }
24384     },
24385
24386     /**
24387      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24388      * @param {Number} startRow The index of the first row in the range
24389      * @param {Number} endRow The index of the last row in the range
24390      * @param {Boolean} keepExisting (optional) True to retain existing selections
24391      */
24392     selectRange : function(startRow, endRow, keepExisting){
24393         if(this.locked) {
24394             return;
24395         }
24396         if(!keepExisting){
24397             this.clearSelections();
24398         }
24399         if(startRow <= endRow){
24400             for(var i = startRow; i <= endRow; i++){
24401                 this.selectRow(i, true);
24402             }
24403         }else{
24404             for(var i = startRow; i >= endRow; i--){
24405                 this.selectRow(i, true);
24406             }
24407         }
24408     },
24409
24410     /**
24411      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24412      * @param {Number} startRow The index of the first row in the range
24413      * @param {Number} endRow The index of the last row in the range
24414      */
24415     deselectRange : function(startRow, endRow, preventViewNotify){
24416         if(this.locked) {
24417             return;
24418         }
24419         for(var i = startRow; i <= endRow; i++){
24420             this.deselectRow(i, preventViewNotify);
24421         }
24422     },
24423
24424     /**
24425      * Selects a row.
24426      * @param {Number} row The index of the row to select
24427      * @param {Boolean} keepExisting (optional) True to keep existing selections
24428      */
24429     selectRow : function(index, keepExisting, preventViewNotify)
24430     {
24431             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24432             return;
24433         }
24434         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24435             if(!keepExisting || this.singleSelect){
24436                 this.clearSelections();
24437             }
24438             
24439             var r = this.grid.store.getAt(index);
24440             //console.log('selectRow - record id :' + r.id);
24441             
24442             this.selections.add(r);
24443             this.last = this.lastActive = index;
24444             if(!preventViewNotify){
24445                 var proxy = new Roo.Element(
24446                                 this.grid.getRowDom(index)
24447                 );
24448                 proxy.addClass('bg-info info');
24449             }
24450             this.fireEvent("rowselect", this, index, r);
24451             this.fireEvent("selectionchange", this);
24452         }
24453     },
24454
24455     /**
24456      * Deselects a row.
24457      * @param {Number} row The index of the row to deselect
24458      */
24459     deselectRow : function(index, preventViewNotify)
24460     {
24461         if(this.locked) {
24462             return;
24463         }
24464         if(this.last == index){
24465             this.last = false;
24466         }
24467         if(this.lastActive == index){
24468             this.lastActive = false;
24469         }
24470         
24471         var r = this.grid.store.getAt(index);
24472         if (!r) {
24473             return;
24474         }
24475         
24476         this.selections.remove(r);
24477         //.console.log('deselectRow - record id :' + r.id);
24478         if(!preventViewNotify){
24479         
24480             var proxy = new Roo.Element(
24481                 this.grid.getRowDom(index)
24482             );
24483             proxy.removeClass('bg-info info');
24484         }
24485         this.fireEvent("rowdeselect", this, index);
24486         this.fireEvent("selectionchange", this);
24487     },
24488
24489     // private
24490     restoreLast : function(){
24491         if(this._last){
24492             this.last = this._last;
24493         }
24494     },
24495
24496     // private
24497     acceptsNav : function(row, col, cm){
24498         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24499     },
24500
24501     // private
24502     onEditorKey : function(field, e){
24503         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24504         if(k == e.TAB){
24505             e.stopEvent();
24506             ed.completeEdit();
24507             if(e.shiftKey){
24508                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24509             }else{
24510                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24511             }
24512         }else if(k == e.ENTER && !e.ctrlKey){
24513             e.stopEvent();
24514             ed.completeEdit();
24515             if(e.shiftKey){
24516                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24517             }else{
24518                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24519             }
24520         }else if(k == e.ESC){
24521             ed.cancelEdit();
24522         }
24523         if(newCell){
24524             g.startEditing(newCell[0], newCell[1]);
24525         }
24526     }
24527 });
24528 /*
24529  * Based on:
24530  * Ext JS Library 1.1.1
24531  * Copyright(c) 2006-2007, Ext JS, LLC.
24532  *
24533  * Originally Released Under LGPL - original licence link has changed is not relivant.
24534  *
24535  * Fork - LGPL
24536  * <script type="text/javascript">
24537  */
24538  
24539 /**
24540  * @class Roo.bootstrap.PagingToolbar
24541  * @extends Roo.bootstrap.NavSimplebar
24542  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24543  * @constructor
24544  * Create a new PagingToolbar
24545  * @param {Object} config The config object
24546  * @param {Roo.data.Store} store
24547  */
24548 Roo.bootstrap.PagingToolbar = function(config)
24549 {
24550     // old args format still supported... - xtype is prefered..
24551         // created from xtype...
24552     
24553     this.ds = config.dataSource;
24554     
24555     if (config.store && !this.ds) {
24556         this.store= Roo.factory(config.store, Roo.data);
24557         this.ds = this.store;
24558         this.ds.xmodule = this.xmodule || false;
24559     }
24560     
24561     this.toolbarItems = [];
24562     if (config.items) {
24563         this.toolbarItems = config.items;
24564     }
24565     
24566     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24567     
24568     this.cursor = 0;
24569     
24570     if (this.ds) { 
24571         this.bind(this.ds);
24572     }
24573     
24574     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24575     
24576 };
24577
24578 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24579     /**
24580      * @cfg {Roo.data.Store} dataSource
24581      * The underlying data store providing the paged data
24582      */
24583     /**
24584      * @cfg {String/HTMLElement/Element} container
24585      * container The id or element that will contain the toolbar
24586      */
24587     /**
24588      * @cfg {Boolean} displayInfo
24589      * True to display the displayMsg (defaults to false)
24590      */
24591     /**
24592      * @cfg {Number} pageSize
24593      * The number of records to display per page (defaults to 20)
24594      */
24595     pageSize: 20,
24596     /**
24597      * @cfg {String} displayMsg
24598      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24599      */
24600     displayMsg : 'Displaying {0} - {1} of {2}',
24601     /**
24602      * @cfg {String} emptyMsg
24603      * The message to display when no records are found (defaults to "No data to display")
24604      */
24605     emptyMsg : 'No data to display',
24606     /**
24607      * Customizable piece of the default paging text (defaults to "Page")
24608      * @type String
24609      */
24610     beforePageText : "Page",
24611     /**
24612      * Customizable piece of the default paging text (defaults to "of %0")
24613      * @type String
24614      */
24615     afterPageText : "of {0}",
24616     /**
24617      * Customizable piece of the default paging text (defaults to "First Page")
24618      * @type String
24619      */
24620     firstText : "First Page",
24621     /**
24622      * Customizable piece of the default paging text (defaults to "Previous Page")
24623      * @type String
24624      */
24625     prevText : "Previous Page",
24626     /**
24627      * Customizable piece of the default paging text (defaults to "Next Page")
24628      * @type String
24629      */
24630     nextText : "Next Page",
24631     /**
24632      * Customizable piece of the default paging text (defaults to "Last Page")
24633      * @type String
24634      */
24635     lastText : "Last Page",
24636     /**
24637      * Customizable piece of the default paging text (defaults to "Refresh")
24638      * @type String
24639      */
24640     refreshText : "Refresh",
24641
24642     buttons : false,
24643     // private
24644     onRender : function(ct, position) 
24645     {
24646         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24647         this.navgroup.parentId = this.id;
24648         this.navgroup.onRender(this.el, null);
24649         // add the buttons to the navgroup
24650         
24651         if(this.displayInfo){
24652             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24653             this.displayEl = this.el.select('.x-paging-info', true).first();
24654 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24655 //            this.displayEl = navel.el.select('span',true).first();
24656         }
24657         
24658         var _this = this;
24659         
24660         if(this.buttons){
24661             Roo.each(_this.buttons, function(e){ // this might need to use render????
24662                Roo.factory(e).render(_this.el);
24663             });
24664         }
24665             
24666         Roo.each(_this.toolbarItems, function(e) {
24667             _this.navgroup.addItem(e);
24668         });
24669         
24670         
24671         this.first = this.navgroup.addItem({
24672             tooltip: this.firstText,
24673             cls: "prev",
24674             icon : 'fa fa-backward',
24675             disabled: true,
24676             preventDefault: true,
24677             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24678         });
24679         
24680         this.prev =  this.navgroup.addItem({
24681             tooltip: this.prevText,
24682             cls: "prev",
24683             icon : 'fa fa-step-backward',
24684             disabled: true,
24685             preventDefault: true,
24686             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24687         });
24688     //this.addSeparator();
24689         
24690         
24691         var field = this.navgroup.addItem( {
24692             tagtype : 'span',
24693             cls : 'x-paging-position',
24694             
24695             html : this.beforePageText  +
24696                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24697                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24698          } ); //?? escaped?
24699         
24700         this.field = field.el.select('input', true).first();
24701         this.field.on("keydown", this.onPagingKeydown, this);
24702         this.field.on("focus", function(){this.dom.select();});
24703     
24704     
24705         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24706         //this.field.setHeight(18);
24707         //this.addSeparator();
24708         this.next = this.navgroup.addItem({
24709             tooltip: this.nextText,
24710             cls: "next",
24711             html : ' <i class="fa fa-step-forward">',
24712             disabled: true,
24713             preventDefault: true,
24714             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24715         });
24716         this.last = this.navgroup.addItem({
24717             tooltip: this.lastText,
24718             icon : 'fa fa-forward',
24719             cls: "next",
24720             disabled: true,
24721             preventDefault: true,
24722             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24723         });
24724     //this.addSeparator();
24725         this.loading = this.navgroup.addItem({
24726             tooltip: this.refreshText,
24727             icon: 'fa fa-refresh',
24728             preventDefault: true,
24729             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24730         });
24731         
24732     },
24733
24734     // private
24735     updateInfo : function(){
24736         if(this.displayEl){
24737             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24738             var msg = count == 0 ?
24739                 this.emptyMsg :
24740                 String.format(
24741                     this.displayMsg,
24742                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24743                 );
24744             this.displayEl.update(msg);
24745         }
24746     },
24747
24748     // private
24749     onLoad : function(ds, r, o)
24750     {
24751         this.cursor = o.params.start ? o.params.start : 0;
24752         
24753         var d = this.getPageData(),
24754             ap = d.activePage,
24755             ps = d.pages;
24756         
24757         
24758         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24759         this.field.dom.value = ap;
24760         this.first.setDisabled(ap == 1);
24761         this.prev.setDisabled(ap == 1);
24762         this.next.setDisabled(ap == ps);
24763         this.last.setDisabled(ap == ps);
24764         this.loading.enable();
24765         this.updateInfo();
24766     },
24767
24768     // private
24769     getPageData : function(){
24770         var total = this.ds.getTotalCount();
24771         return {
24772             total : total,
24773             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24774             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24775         };
24776     },
24777
24778     // private
24779     onLoadError : function(){
24780         this.loading.enable();
24781     },
24782
24783     // private
24784     onPagingKeydown : function(e){
24785         var k = e.getKey();
24786         var d = this.getPageData();
24787         if(k == e.RETURN){
24788             var v = this.field.dom.value, pageNum;
24789             if(!v || isNaN(pageNum = parseInt(v, 10))){
24790                 this.field.dom.value = d.activePage;
24791                 return;
24792             }
24793             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24794             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24795             e.stopEvent();
24796         }
24797         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))
24798         {
24799           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24800           this.field.dom.value = pageNum;
24801           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24802           e.stopEvent();
24803         }
24804         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24805         {
24806           var v = this.field.dom.value, pageNum; 
24807           var increment = (e.shiftKey) ? 10 : 1;
24808           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24809                 increment *= -1;
24810           }
24811           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24812             this.field.dom.value = d.activePage;
24813             return;
24814           }
24815           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24816           {
24817             this.field.dom.value = parseInt(v, 10) + increment;
24818             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24819             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24820           }
24821           e.stopEvent();
24822         }
24823     },
24824
24825     // private
24826     beforeLoad : function(){
24827         if(this.loading){
24828             this.loading.disable();
24829         }
24830     },
24831
24832     // private
24833     onClick : function(which){
24834         
24835         var ds = this.ds;
24836         if (!ds) {
24837             return;
24838         }
24839         
24840         switch(which){
24841             case "first":
24842                 ds.load({params:{start: 0, limit: this.pageSize}});
24843             break;
24844             case "prev":
24845                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24846             break;
24847             case "next":
24848                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24849             break;
24850             case "last":
24851                 var total = ds.getTotalCount();
24852                 var extra = total % this.pageSize;
24853                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24854                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24855             break;
24856             case "refresh":
24857                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24858             break;
24859         }
24860     },
24861
24862     /**
24863      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24864      * @param {Roo.data.Store} store The data store to unbind
24865      */
24866     unbind : function(ds){
24867         ds.un("beforeload", this.beforeLoad, this);
24868         ds.un("load", this.onLoad, this);
24869         ds.un("loadexception", this.onLoadError, this);
24870         ds.un("remove", this.updateInfo, this);
24871         ds.un("add", this.updateInfo, this);
24872         this.ds = undefined;
24873     },
24874
24875     /**
24876      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24877      * @param {Roo.data.Store} store The data store to bind
24878      */
24879     bind : function(ds){
24880         ds.on("beforeload", this.beforeLoad, this);
24881         ds.on("load", this.onLoad, this);
24882         ds.on("loadexception", this.onLoadError, this);
24883         ds.on("remove", this.updateInfo, this);
24884         ds.on("add", this.updateInfo, this);
24885         this.ds = ds;
24886     }
24887 });/*
24888  * - LGPL
24889  *
24890  * element
24891  * 
24892  */
24893
24894 /**
24895  * @class Roo.bootstrap.MessageBar
24896  * @extends Roo.bootstrap.Component
24897  * Bootstrap MessageBar class
24898  * @cfg {String} html contents of the MessageBar
24899  * @cfg {String} weight (info | success | warning | danger) default info
24900  * @cfg {String} beforeClass insert the bar before the given class
24901  * @cfg {Boolean} closable (true | false) default false
24902  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24903  * 
24904  * @constructor
24905  * Create a new Element
24906  * @param {Object} config The config object
24907  */
24908
24909 Roo.bootstrap.MessageBar = function(config){
24910     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24911 };
24912
24913 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24914     
24915     html: '',
24916     weight: 'info',
24917     closable: false,
24918     fixed: false,
24919     beforeClass: 'bootstrap-sticky-wrap',
24920     
24921     getAutoCreate : function(){
24922         
24923         var cfg = {
24924             tag: 'div',
24925             cls: 'alert alert-dismissable alert-' + this.weight,
24926             cn: [
24927                 {
24928                     tag: 'span',
24929                     cls: 'message',
24930                     html: this.html || ''
24931                 }
24932             ]
24933         };
24934         
24935         if(this.fixed){
24936             cfg.cls += ' alert-messages-fixed';
24937         }
24938         
24939         if(this.closable){
24940             cfg.cn.push({
24941                 tag: 'button',
24942                 cls: 'close',
24943                 html: 'x'
24944             });
24945         }
24946         
24947         return cfg;
24948     },
24949     
24950     onRender : function(ct, position)
24951     {
24952         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24953         
24954         if(!this.el){
24955             var cfg = Roo.apply({},  this.getAutoCreate());
24956             cfg.id = Roo.id();
24957             
24958             if (this.cls) {
24959                 cfg.cls += ' ' + this.cls;
24960             }
24961             if (this.style) {
24962                 cfg.style = this.style;
24963             }
24964             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24965             
24966             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24967         }
24968         
24969         this.el.select('>button.close').on('click', this.hide, this);
24970         
24971     },
24972     
24973     show : function()
24974     {
24975         if (!this.rendered) {
24976             this.render();
24977         }
24978         
24979         this.el.show();
24980         
24981         this.fireEvent('show', this);
24982         
24983     },
24984     
24985     hide : function()
24986     {
24987         if (!this.rendered) {
24988             this.render();
24989         }
24990         
24991         this.el.hide();
24992         
24993         this.fireEvent('hide', this);
24994     },
24995     
24996     update : function()
24997     {
24998 //        var e = this.el.dom.firstChild;
24999 //        
25000 //        if(this.closable){
25001 //            e = e.nextSibling;
25002 //        }
25003 //        
25004 //        e.data = this.html || '';
25005
25006         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25007     }
25008    
25009 });
25010
25011  
25012
25013      /*
25014  * - LGPL
25015  *
25016  * Graph
25017  * 
25018  */
25019
25020
25021 /**
25022  * @class Roo.bootstrap.Graph
25023  * @extends Roo.bootstrap.Component
25024  * Bootstrap Graph class
25025 > Prameters
25026  -sm {number} sm 4
25027  -md {number} md 5
25028  @cfg {String} graphtype  bar | vbar | pie
25029  @cfg {number} g_x coodinator | centre x (pie)
25030  @cfg {number} g_y coodinator | centre y (pie)
25031  @cfg {number} g_r radius (pie)
25032  @cfg {number} g_height height of the chart (respected by all elements in the set)
25033  @cfg {number} g_width width of the chart (respected by all elements in the set)
25034  @cfg {Object} title The title of the chart
25035     
25036  -{Array}  values
25037  -opts (object) options for the chart 
25038      o {
25039      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25040      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25041      o vgutter (number)
25042      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.
25043      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25044      o to
25045      o stretch (boolean)
25046      o }
25047  -opts (object) options for the pie
25048      o{
25049      o cut
25050      o startAngle (number)
25051      o endAngle (number)
25052      } 
25053  *
25054  * @constructor
25055  * Create a new Input
25056  * @param {Object} config The config object
25057  */
25058
25059 Roo.bootstrap.Graph = function(config){
25060     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25061     
25062     this.addEvents({
25063         // img events
25064         /**
25065          * @event click
25066          * The img click event for the img.
25067          * @param {Roo.EventObject} e
25068          */
25069         "click" : true
25070     });
25071 };
25072
25073 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25074     
25075     sm: 4,
25076     md: 5,
25077     graphtype: 'bar',
25078     g_height: 250,
25079     g_width: 400,
25080     g_x: 50,
25081     g_y: 50,
25082     g_r: 30,
25083     opts:{
25084         //g_colors: this.colors,
25085         g_type: 'soft',
25086         g_gutter: '20%'
25087
25088     },
25089     title : false,
25090
25091     getAutoCreate : function(){
25092         
25093         var cfg = {
25094             tag: 'div',
25095             html : null
25096         };
25097         
25098         
25099         return  cfg;
25100     },
25101
25102     onRender : function(ct,position){
25103         
25104         
25105         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25106         
25107         if (typeof(Raphael) == 'undefined') {
25108             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25109             return;
25110         }
25111         
25112         this.raphael = Raphael(this.el.dom);
25113         
25114                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25115                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25116                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25117                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25118                 /*
25119                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25120                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25121                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25122                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25123                 
25124                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25125                 r.barchart(330, 10, 300, 220, data1);
25126                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25127                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25128                 */
25129                 
25130                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25131                 // r.barchart(30, 30, 560, 250,  xdata, {
25132                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25133                 //     axis : "0 0 1 1",
25134                 //     axisxlabels :  xdata
25135                 //     //yvalues : cols,
25136                    
25137                 // });
25138 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25139 //        
25140 //        this.load(null,xdata,{
25141 //                axis : "0 0 1 1",
25142 //                axisxlabels :  xdata
25143 //                });
25144
25145     },
25146
25147     load : function(graphtype,xdata,opts)
25148     {
25149         this.raphael.clear();
25150         if(!graphtype) {
25151             graphtype = this.graphtype;
25152         }
25153         if(!opts){
25154             opts = this.opts;
25155         }
25156         var r = this.raphael,
25157             fin = function () {
25158                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25159             },
25160             fout = function () {
25161                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25162             },
25163             pfin = function() {
25164                 this.sector.stop();
25165                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25166
25167                 if (this.label) {
25168                     this.label[0].stop();
25169                     this.label[0].attr({ r: 7.5 });
25170                     this.label[1].attr({ "font-weight": 800 });
25171                 }
25172             },
25173             pfout = function() {
25174                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25175
25176                 if (this.label) {
25177                     this.label[0].animate({ r: 5 }, 500, "bounce");
25178                     this.label[1].attr({ "font-weight": 400 });
25179                 }
25180             };
25181
25182         switch(graphtype){
25183             case 'bar':
25184                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25185                 break;
25186             case 'hbar':
25187                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25188                 break;
25189             case 'pie':
25190 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25191 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25192 //            
25193                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25194                 
25195                 break;
25196
25197         }
25198         
25199         if(this.title){
25200             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25201         }
25202         
25203     },
25204     
25205     setTitle: function(o)
25206     {
25207         this.title = o;
25208     },
25209     
25210     initEvents: function() {
25211         
25212         if(!this.href){
25213             this.el.on('click', this.onClick, this);
25214         }
25215     },
25216     
25217     onClick : function(e)
25218     {
25219         Roo.log('img onclick');
25220         this.fireEvent('click', this, e);
25221     }
25222    
25223 });
25224
25225  
25226 /*
25227  * - LGPL
25228  *
25229  * numberBox
25230  * 
25231  */
25232 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25233
25234 /**
25235  * @class Roo.bootstrap.dash.NumberBox
25236  * @extends Roo.bootstrap.Component
25237  * Bootstrap NumberBox class
25238  * @cfg {String} headline Box headline
25239  * @cfg {String} content Box content
25240  * @cfg {String} icon Box icon
25241  * @cfg {String} footer Footer text
25242  * @cfg {String} fhref Footer href
25243  * 
25244  * @constructor
25245  * Create a new NumberBox
25246  * @param {Object} config The config object
25247  */
25248
25249
25250 Roo.bootstrap.dash.NumberBox = function(config){
25251     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25252     
25253 };
25254
25255 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25256     
25257     headline : '',
25258     content : '',
25259     icon : '',
25260     footer : '',
25261     fhref : '',
25262     ficon : '',
25263     
25264     getAutoCreate : function(){
25265         
25266         var cfg = {
25267             tag : 'div',
25268             cls : 'small-box ',
25269             cn : [
25270                 {
25271                     tag : 'div',
25272                     cls : 'inner',
25273                     cn :[
25274                         {
25275                             tag : 'h3',
25276                             cls : 'roo-headline',
25277                             html : this.headline
25278                         },
25279                         {
25280                             tag : 'p',
25281                             cls : 'roo-content',
25282                             html : this.content
25283                         }
25284                     ]
25285                 }
25286             ]
25287         };
25288         
25289         if(this.icon){
25290             cfg.cn.push({
25291                 tag : 'div',
25292                 cls : 'icon',
25293                 cn :[
25294                     {
25295                         tag : 'i',
25296                         cls : 'ion ' + this.icon
25297                     }
25298                 ]
25299             });
25300         }
25301         
25302         if(this.footer){
25303             var footer = {
25304                 tag : 'a',
25305                 cls : 'small-box-footer',
25306                 href : this.fhref || '#',
25307                 html : this.footer
25308             };
25309             
25310             cfg.cn.push(footer);
25311             
25312         }
25313         
25314         return  cfg;
25315     },
25316
25317     onRender : function(ct,position){
25318         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25319
25320
25321        
25322                 
25323     },
25324
25325     setHeadline: function (value)
25326     {
25327         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25328     },
25329     
25330     setFooter: function (value, href)
25331     {
25332         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25333         
25334         if(href){
25335             this.el.select('a.small-box-footer',true).first().attr('href', href);
25336         }
25337         
25338     },
25339
25340     setContent: function (value)
25341     {
25342         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25343     },
25344
25345     initEvents: function() 
25346     {   
25347         
25348     }
25349     
25350 });
25351
25352  
25353 /*
25354  * - LGPL
25355  *
25356  * TabBox
25357  * 
25358  */
25359 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25360
25361 /**
25362  * @class Roo.bootstrap.dash.TabBox
25363  * @extends Roo.bootstrap.Component
25364  * Bootstrap TabBox class
25365  * @cfg {String} title Title of the TabBox
25366  * @cfg {String} icon Icon of the TabBox
25367  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25368  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25369  * 
25370  * @constructor
25371  * Create a new TabBox
25372  * @param {Object} config The config object
25373  */
25374
25375
25376 Roo.bootstrap.dash.TabBox = function(config){
25377     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25378     this.addEvents({
25379         // raw events
25380         /**
25381          * @event addpane
25382          * When a pane is added
25383          * @param {Roo.bootstrap.dash.TabPane} pane
25384          */
25385         "addpane" : true,
25386         /**
25387          * @event activatepane
25388          * When a pane is activated
25389          * @param {Roo.bootstrap.dash.TabPane} pane
25390          */
25391         "activatepane" : true
25392         
25393          
25394     });
25395     
25396     this.panes = [];
25397 };
25398
25399 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25400
25401     title : '',
25402     icon : false,
25403     showtabs : true,
25404     tabScrollable : false,
25405     
25406     getChildContainer : function()
25407     {
25408         return this.el.select('.tab-content', true).first();
25409     },
25410     
25411     getAutoCreate : function(){
25412         
25413         var header = {
25414             tag: 'li',
25415             cls: 'pull-left header',
25416             html: this.title,
25417             cn : []
25418         };
25419         
25420         if(this.icon){
25421             header.cn.push({
25422                 tag: 'i',
25423                 cls: 'fa ' + this.icon
25424             });
25425         }
25426         
25427         var h = {
25428             tag: 'ul',
25429             cls: 'nav nav-tabs pull-right',
25430             cn: [
25431                 header
25432             ]
25433         };
25434         
25435         if(this.tabScrollable){
25436             h = {
25437                 tag: 'div',
25438                 cls: 'tab-header',
25439                 cn: [
25440                     {
25441                         tag: 'ul',
25442                         cls: 'nav nav-tabs pull-right',
25443                         cn: [
25444                             header
25445                         ]
25446                     }
25447                 ]
25448             };
25449         }
25450         
25451         var cfg = {
25452             tag: 'div',
25453             cls: 'nav-tabs-custom',
25454             cn: [
25455                 h,
25456                 {
25457                     tag: 'div',
25458                     cls: 'tab-content no-padding',
25459                     cn: []
25460                 }
25461             ]
25462         };
25463
25464         return  cfg;
25465     },
25466     initEvents : function()
25467     {
25468         //Roo.log('add add pane handler');
25469         this.on('addpane', this.onAddPane, this);
25470     },
25471      /**
25472      * Updates the box title
25473      * @param {String} html to set the title to.
25474      */
25475     setTitle : function(value)
25476     {
25477         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25478     },
25479     onAddPane : function(pane)
25480     {
25481         this.panes.push(pane);
25482         //Roo.log('addpane');
25483         //Roo.log(pane);
25484         // tabs are rendere left to right..
25485         if(!this.showtabs){
25486             return;
25487         }
25488         
25489         var ctr = this.el.select('.nav-tabs', true).first();
25490          
25491          
25492         var existing = ctr.select('.nav-tab',true);
25493         var qty = existing.getCount();;
25494         
25495         
25496         var tab = ctr.createChild({
25497             tag : 'li',
25498             cls : 'nav-tab' + (qty ? '' : ' active'),
25499             cn : [
25500                 {
25501                     tag : 'a',
25502                     href:'#',
25503                     html : pane.title
25504                 }
25505             ]
25506         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25507         pane.tab = tab;
25508         
25509         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25510         if (!qty) {
25511             pane.el.addClass('active');
25512         }
25513         
25514                 
25515     },
25516     onTabClick : function(ev,un,ob,pane)
25517     {
25518         //Roo.log('tab - prev default');
25519         ev.preventDefault();
25520         
25521         
25522         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25523         pane.tab.addClass('active');
25524         //Roo.log(pane.title);
25525         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25526         // technically we should have a deactivate event.. but maybe add later.
25527         // and it should not de-activate the selected tab...
25528         this.fireEvent('activatepane', pane);
25529         pane.el.addClass('active');
25530         pane.fireEvent('activate');
25531         
25532         
25533     },
25534     
25535     getActivePane : function()
25536     {
25537         var r = false;
25538         Roo.each(this.panes, function(p) {
25539             if(p.el.hasClass('active')){
25540                 r = p;
25541                 return false;
25542             }
25543             
25544             return;
25545         });
25546         
25547         return r;
25548     }
25549     
25550     
25551 });
25552
25553  
25554 /*
25555  * - LGPL
25556  *
25557  * Tab pane
25558  * 
25559  */
25560 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25561 /**
25562  * @class Roo.bootstrap.TabPane
25563  * @extends Roo.bootstrap.Component
25564  * Bootstrap TabPane class
25565  * @cfg {Boolean} active (false | true) Default false
25566  * @cfg {String} title title of panel
25567
25568  * 
25569  * @constructor
25570  * Create a new TabPane
25571  * @param {Object} config The config object
25572  */
25573
25574 Roo.bootstrap.dash.TabPane = function(config){
25575     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25576     
25577     this.addEvents({
25578         // raw events
25579         /**
25580          * @event activate
25581          * When a pane is activated
25582          * @param {Roo.bootstrap.dash.TabPane} pane
25583          */
25584         "activate" : true
25585          
25586     });
25587 };
25588
25589 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25590     
25591     active : false,
25592     title : '',
25593     
25594     // the tabBox that this is attached to.
25595     tab : false,
25596      
25597     getAutoCreate : function() 
25598     {
25599         var cfg = {
25600             tag: 'div',
25601             cls: 'tab-pane'
25602         };
25603         
25604         if(this.active){
25605             cfg.cls += ' active';
25606         }
25607         
25608         return cfg;
25609     },
25610     initEvents  : function()
25611     {
25612         //Roo.log('trigger add pane handler');
25613         this.parent().fireEvent('addpane', this)
25614     },
25615     
25616      /**
25617      * Updates the tab title 
25618      * @param {String} html to set the title to.
25619      */
25620     setTitle: function(str)
25621     {
25622         if (!this.tab) {
25623             return;
25624         }
25625         this.title = str;
25626         this.tab.select('a', true).first().dom.innerHTML = str;
25627         
25628     }
25629     
25630     
25631     
25632 });
25633
25634  
25635
25636
25637  /*
25638  * - LGPL
25639  *
25640  * menu
25641  * 
25642  */
25643 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25644
25645 /**
25646  * @class Roo.bootstrap.menu.Menu
25647  * @extends Roo.bootstrap.Component
25648  * Bootstrap Menu class - container for Menu
25649  * @cfg {String} html Text of the menu
25650  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25651  * @cfg {String} icon Font awesome icon
25652  * @cfg {String} pos Menu align to (top | bottom) default bottom
25653  * 
25654  * 
25655  * @constructor
25656  * Create a new Menu
25657  * @param {Object} config The config object
25658  */
25659
25660
25661 Roo.bootstrap.menu.Menu = function(config){
25662     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25663     
25664     this.addEvents({
25665         /**
25666          * @event beforeshow
25667          * Fires before this menu is displayed
25668          * @param {Roo.bootstrap.menu.Menu} this
25669          */
25670         beforeshow : true,
25671         /**
25672          * @event beforehide
25673          * Fires before this menu is hidden
25674          * @param {Roo.bootstrap.menu.Menu} this
25675          */
25676         beforehide : true,
25677         /**
25678          * @event show
25679          * Fires after this menu is displayed
25680          * @param {Roo.bootstrap.menu.Menu} this
25681          */
25682         show : true,
25683         /**
25684          * @event hide
25685          * Fires after this menu is hidden
25686          * @param {Roo.bootstrap.menu.Menu} this
25687          */
25688         hide : true,
25689         /**
25690          * @event click
25691          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25692          * @param {Roo.bootstrap.menu.Menu} this
25693          * @param {Roo.EventObject} e
25694          */
25695         click : true
25696     });
25697     
25698 };
25699
25700 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25701     
25702     submenu : false,
25703     html : '',
25704     weight : 'default',
25705     icon : false,
25706     pos : 'bottom',
25707     
25708     
25709     getChildContainer : function() {
25710         if(this.isSubMenu){
25711             return this.el;
25712         }
25713         
25714         return this.el.select('ul.dropdown-menu', true).first();  
25715     },
25716     
25717     getAutoCreate : function()
25718     {
25719         var text = [
25720             {
25721                 tag : 'span',
25722                 cls : 'roo-menu-text',
25723                 html : this.html
25724             }
25725         ];
25726         
25727         if(this.icon){
25728             text.unshift({
25729                 tag : 'i',
25730                 cls : 'fa ' + this.icon
25731             })
25732         }
25733         
25734         
25735         var cfg = {
25736             tag : 'div',
25737             cls : 'btn-group',
25738             cn : [
25739                 {
25740                     tag : 'button',
25741                     cls : 'dropdown-button btn btn-' + this.weight,
25742                     cn : text
25743                 },
25744                 {
25745                     tag : 'button',
25746                     cls : 'dropdown-toggle btn btn-' + this.weight,
25747                     cn : [
25748                         {
25749                             tag : 'span',
25750                             cls : 'caret'
25751                         }
25752                     ]
25753                 },
25754                 {
25755                     tag : 'ul',
25756                     cls : 'dropdown-menu'
25757                 }
25758             ]
25759             
25760         };
25761         
25762         if(this.pos == 'top'){
25763             cfg.cls += ' dropup';
25764         }
25765         
25766         if(this.isSubMenu){
25767             cfg = {
25768                 tag : 'ul',
25769                 cls : 'dropdown-menu'
25770             }
25771         }
25772         
25773         return cfg;
25774     },
25775     
25776     onRender : function(ct, position)
25777     {
25778         this.isSubMenu = ct.hasClass('dropdown-submenu');
25779         
25780         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25781     },
25782     
25783     initEvents : function() 
25784     {
25785         if(this.isSubMenu){
25786             return;
25787         }
25788         
25789         this.hidden = true;
25790         
25791         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25792         this.triggerEl.on('click', this.onTriggerPress, this);
25793         
25794         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25795         this.buttonEl.on('click', this.onClick, this);
25796         
25797     },
25798     
25799     list : function()
25800     {
25801         if(this.isSubMenu){
25802             return this.el;
25803         }
25804         
25805         return this.el.select('ul.dropdown-menu', true).first();
25806     },
25807     
25808     onClick : function(e)
25809     {
25810         this.fireEvent("click", this, e);
25811     },
25812     
25813     onTriggerPress  : function(e)
25814     {   
25815         if (this.isVisible()) {
25816             this.hide();
25817         } else {
25818             this.show();
25819         }
25820     },
25821     
25822     isVisible : function(){
25823         return !this.hidden;
25824     },
25825     
25826     show : function()
25827     {
25828         this.fireEvent("beforeshow", this);
25829         
25830         this.hidden = false;
25831         this.el.addClass('open');
25832         
25833         Roo.get(document).on("mouseup", this.onMouseUp, this);
25834         
25835         this.fireEvent("show", this);
25836         
25837         
25838     },
25839     
25840     hide : function()
25841     {
25842         this.fireEvent("beforehide", this);
25843         
25844         this.hidden = true;
25845         this.el.removeClass('open');
25846         
25847         Roo.get(document).un("mouseup", this.onMouseUp);
25848         
25849         this.fireEvent("hide", this);
25850     },
25851     
25852     onMouseUp : function()
25853     {
25854         this.hide();
25855     }
25856     
25857 });
25858
25859  
25860  /*
25861  * - LGPL
25862  *
25863  * menu item
25864  * 
25865  */
25866 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25867
25868 /**
25869  * @class Roo.bootstrap.menu.Item
25870  * @extends Roo.bootstrap.Component
25871  * Bootstrap MenuItem class
25872  * @cfg {Boolean} submenu (true | false) default false
25873  * @cfg {String} html text of the item
25874  * @cfg {String} href the link
25875  * @cfg {Boolean} disable (true | false) default false
25876  * @cfg {Boolean} preventDefault (true | false) default true
25877  * @cfg {String} icon Font awesome icon
25878  * @cfg {String} pos Submenu align to (left | right) default right 
25879  * 
25880  * 
25881  * @constructor
25882  * Create a new Item
25883  * @param {Object} config The config object
25884  */
25885
25886
25887 Roo.bootstrap.menu.Item = function(config){
25888     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25889     this.addEvents({
25890         /**
25891          * @event mouseover
25892          * Fires when the mouse is hovering over this menu
25893          * @param {Roo.bootstrap.menu.Item} this
25894          * @param {Roo.EventObject} e
25895          */
25896         mouseover : true,
25897         /**
25898          * @event mouseout
25899          * Fires when the mouse exits this menu
25900          * @param {Roo.bootstrap.menu.Item} this
25901          * @param {Roo.EventObject} e
25902          */
25903         mouseout : true,
25904         // raw events
25905         /**
25906          * @event click
25907          * The raw click event for the entire grid.
25908          * @param {Roo.EventObject} e
25909          */
25910         click : true
25911     });
25912 };
25913
25914 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25915     
25916     submenu : false,
25917     href : '',
25918     html : '',
25919     preventDefault: true,
25920     disable : false,
25921     icon : false,
25922     pos : 'right',
25923     
25924     getAutoCreate : function()
25925     {
25926         var text = [
25927             {
25928                 tag : 'span',
25929                 cls : 'roo-menu-item-text',
25930                 html : this.html
25931             }
25932         ];
25933         
25934         if(this.icon){
25935             text.unshift({
25936                 tag : 'i',
25937                 cls : 'fa ' + this.icon
25938             })
25939         }
25940         
25941         var cfg = {
25942             tag : 'li',
25943             cn : [
25944                 {
25945                     tag : 'a',
25946                     href : this.href || '#',
25947                     cn : text
25948                 }
25949             ]
25950         };
25951         
25952         if(this.disable){
25953             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25954         }
25955         
25956         if(this.submenu){
25957             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25958             
25959             if(this.pos == 'left'){
25960                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25961             }
25962         }
25963         
25964         return cfg;
25965     },
25966     
25967     initEvents : function() 
25968     {
25969         this.el.on('mouseover', this.onMouseOver, this);
25970         this.el.on('mouseout', this.onMouseOut, this);
25971         
25972         this.el.select('a', true).first().on('click', this.onClick, this);
25973         
25974     },
25975     
25976     onClick : function(e)
25977     {
25978         if(this.preventDefault){
25979             e.preventDefault();
25980         }
25981         
25982         this.fireEvent("click", this, e);
25983     },
25984     
25985     onMouseOver : function(e)
25986     {
25987         if(this.submenu && this.pos == 'left'){
25988             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25989         }
25990         
25991         this.fireEvent("mouseover", this, e);
25992     },
25993     
25994     onMouseOut : function(e)
25995     {
25996         this.fireEvent("mouseout", this, e);
25997     }
25998 });
25999
26000  
26001
26002  /*
26003  * - LGPL
26004  *
26005  * menu separator
26006  * 
26007  */
26008 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26009
26010 /**
26011  * @class Roo.bootstrap.menu.Separator
26012  * @extends Roo.bootstrap.Component
26013  * Bootstrap Separator class
26014  * 
26015  * @constructor
26016  * Create a new Separator
26017  * @param {Object} config The config object
26018  */
26019
26020
26021 Roo.bootstrap.menu.Separator = function(config){
26022     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26023 };
26024
26025 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26026     
26027     getAutoCreate : function(){
26028         var cfg = {
26029             tag : 'li',
26030             cls: 'divider'
26031         };
26032         
26033         return cfg;
26034     }
26035    
26036 });
26037
26038  
26039
26040  /*
26041  * - LGPL
26042  *
26043  * Tooltip
26044  * 
26045  */
26046
26047 /**
26048  * @class Roo.bootstrap.Tooltip
26049  * Bootstrap Tooltip class
26050  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26051  * to determine which dom element triggers the tooltip.
26052  * 
26053  * It needs to add support for additional attributes like tooltip-position
26054  * 
26055  * @constructor
26056  * Create a new Toolti
26057  * @param {Object} config The config object
26058  */
26059
26060 Roo.bootstrap.Tooltip = function(config){
26061     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26062     
26063     this.alignment = Roo.bootstrap.Tooltip.alignment;
26064     
26065     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26066         this.alignment = config.alignment;
26067     }
26068     
26069 };
26070
26071 Roo.apply(Roo.bootstrap.Tooltip, {
26072     /**
26073      * @function init initialize tooltip monitoring.
26074      * @static
26075      */
26076     currentEl : false,
26077     currentTip : false,
26078     currentRegion : false,
26079     
26080     //  init : delay?
26081     
26082     init : function()
26083     {
26084         Roo.get(document).on('mouseover', this.enter ,this);
26085         Roo.get(document).on('mouseout', this.leave, this);
26086          
26087         
26088         this.currentTip = new Roo.bootstrap.Tooltip();
26089     },
26090     
26091     enter : function(ev)
26092     {
26093         var dom = ev.getTarget();
26094         
26095         //Roo.log(['enter',dom]);
26096         var el = Roo.fly(dom);
26097         if (this.currentEl) {
26098             //Roo.log(dom);
26099             //Roo.log(this.currentEl);
26100             //Roo.log(this.currentEl.contains(dom));
26101             if (this.currentEl == el) {
26102                 return;
26103             }
26104             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26105                 return;
26106             }
26107
26108         }
26109         
26110         if (this.currentTip.el) {
26111             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26112         }    
26113         //Roo.log(ev);
26114         
26115         if(!el || el.dom == document){
26116             return;
26117         }
26118         
26119         var bindEl = el;
26120         
26121         // you can not look for children, as if el is the body.. then everythign is the child..
26122         if (!el.attr('tooltip')) { //
26123             if (!el.select("[tooltip]").elements.length) {
26124                 return;
26125             }
26126             // is the mouse over this child...?
26127             bindEl = el.select("[tooltip]").first();
26128             var xy = ev.getXY();
26129             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26130                 //Roo.log("not in region.");
26131                 return;
26132             }
26133             //Roo.log("child element over..");
26134             
26135         }
26136         this.currentEl = bindEl;
26137         this.currentTip.bind(bindEl);
26138         this.currentRegion = Roo.lib.Region.getRegion(dom);
26139         this.currentTip.enter();
26140         
26141     },
26142     leave : function(ev)
26143     {
26144         var dom = ev.getTarget();
26145         //Roo.log(['leave',dom]);
26146         if (!this.currentEl) {
26147             return;
26148         }
26149         
26150         
26151         if (dom != this.currentEl.dom) {
26152             return;
26153         }
26154         var xy = ev.getXY();
26155         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26156             return;
26157         }
26158         // only activate leave if mouse cursor is outside... bounding box..
26159         
26160         
26161         
26162         
26163         if (this.currentTip) {
26164             this.currentTip.leave();
26165         }
26166         //Roo.log('clear currentEl');
26167         this.currentEl = false;
26168         
26169         
26170     },
26171     alignment : {
26172         'left' : ['r-l', [-2,0], 'right'],
26173         'right' : ['l-r', [2,0], 'left'],
26174         'bottom' : ['t-b', [0,2], 'top'],
26175         'top' : [ 'b-t', [0,-2], 'bottom']
26176     }
26177     
26178 });
26179
26180
26181 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26182     
26183     
26184     bindEl : false,
26185     
26186     delay : null, // can be { show : 300 , hide: 500}
26187     
26188     timeout : null,
26189     
26190     hoverState : null, //???
26191     
26192     placement : 'bottom', 
26193     
26194     alignment : false,
26195     
26196     getAutoCreate : function(){
26197     
26198         var cfg = {
26199            cls : 'tooltip',
26200            role : 'tooltip',
26201            cn : [
26202                 {
26203                     cls : 'tooltip-arrow'
26204                 },
26205                 {
26206                     cls : 'tooltip-inner'
26207                 }
26208            ]
26209         };
26210         
26211         return cfg;
26212     },
26213     bind : function(el)
26214     {
26215         this.bindEl = el;
26216     },
26217       
26218     
26219     enter : function () {
26220        
26221         if (this.timeout != null) {
26222             clearTimeout(this.timeout);
26223         }
26224         
26225         this.hoverState = 'in';
26226          //Roo.log("enter - show");
26227         if (!this.delay || !this.delay.show) {
26228             this.show();
26229             return;
26230         }
26231         var _t = this;
26232         this.timeout = setTimeout(function () {
26233             if (_t.hoverState == 'in') {
26234                 _t.show();
26235             }
26236         }, this.delay.show);
26237     },
26238     leave : function()
26239     {
26240         clearTimeout(this.timeout);
26241     
26242         this.hoverState = 'out';
26243          if (!this.delay || !this.delay.hide) {
26244             this.hide();
26245             return;
26246         }
26247        
26248         var _t = this;
26249         this.timeout = setTimeout(function () {
26250             //Roo.log("leave - timeout");
26251             
26252             if (_t.hoverState == 'out') {
26253                 _t.hide();
26254                 Roo.bootstrap.Tooltip.currentEl = false;
26255             }
26256         }, delay);
26257     },
26258     
26259     show : function (msg)
26260     {
26261         if (!this.el) {
26262             this.render(document.body);
26263         }
26264         // set content.
26265         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26266         
26267         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26268         
26269         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26270         
26271         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26272         
26273         var placement = typeof this.placement == 'function' ?
26274             this.placement.call(this, this.el, on_el) :
26275             this.placement;
26276             
26277         var autoToken = /\s?auto?\s?/i;
26278         var autoPlace = autoToken.test(placement);
26279         if (autoPlace) {
26280             placement = placement.replace(autoToken, '') || 'top';
26281         }
26282         
26283         //this.el.detach()
26284         //this.el.setXY([0,0]);
26285         this.el.show();
26286         //this.el.dom.style.display='block';
26287         
26288         //this.el.appendTo(on_el);
26289         
26290         var p = this.getPosition();
26291         var box = this.el.getBox();
26292         
26293         if (autoPlace) {
26294             // fixme..
26295         }
26296         
26297         var align = this.alignment[placement];
26298         
26299         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26300         
26301         if(placement == 'top' || placement == 'bottom'){
26302             if(xy[0] < 0){
26303                 placement = 'right';
26304             }
26305             
26306             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26307                 placement = 'left';
26308             }
26309             
26310             var scroll = Roo.select('body', true).first().getScroll();
26311             
26312             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26313                 placement = 'top';
26314             }
26315             
26316             align = this.alignment[placement];
26317         }
26318         
26319         this.el.alignTo(this.bindEl, align[0],align[1]);
26320         //var arrow = this.el.select('.arrow',true).first();
26321         //arrow.set(align[2], 
26322         
26323         this.el.addClass(placement);
26324         
26325         this.el.addClass('in fade');
26326         
26327         this.hoverState = null;
26328         
26329         if (this.el.hasClass('fade')) {
26330             // fade it?
26331         }
26332         
26333     },
26334     hide : function()
26335     {
26336          
26337         if (!this.el) {
26338             return;
26339         }
26340         //this.el.setXY([0,0]);
26341         this.el.removeClass('in');
26342         //this.el.hide();
26343         
26344     }
26345     
26346 });
26347  
26348
26349  /*
26350  * - LGPL
26351  *
26352  * Location Picker
26353  * 
26354  */
26355
26356 /**
26357  * @class Roo.bootstrap.LocationPicker
26358  * @extends Roo.bootstrap.Component
26359  * Bootstrap LocationPicker class
26360  * @cfg {Number} latitude Position when init default 0
26361  * @cfg {Number} longitude Position when init default 0
26362  * @cfg {Number} zoom default 15
26363  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26364  * @cfg {Boolean} mapTypeControl default false
26365  * @cfg {Boolean} disableDoubleClickZoom default false
26366  * @cfg {Boolean} scrollwheel default true
26367  * @cfg {Boolean} streetViewControl default false
26368  * @cfg {Number} radius default 0
26369  * @cfg {String} locationName
26370  * @cfg {Boolean} draggable default true
26371  * @cfg {Boolean} enableAutocomplete default false
26372  * @cfg {Boolean} enableReverseGeocode default true
26373  * @cfg {String} markerTitle
26374  * 
26375  * @constructor
26376  * Create a new LocationPicker
26377  * @param {Object} config The config object
26378  */
26379
26380
26381 Roo.bootstrap.LocationPicker = function(config){
26382     
26383     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26384     
26385     this.addEvents({
26386         /**
26387          * @event initial
26388          * Fires when the picker initialized.
26389          * @param {Roo.bootstrap.LocationPicker} this
26390          * @param {Google Location} location
26391          */
26392         initial : true,
26393         /**
26394          * @event positionchanged
26395          * Fires when the picker position changed.
26396          * @param {Roo.bootstrap.LocationPicker} this
26397          * @param {Google Location} location
26398          */
26399         positionchanged : true,
26400         /**
26401          * @event resize
26402          * Fires when the map resize.
26403          * @param {Roo.bootstrap.LocationPicker} this
26404          */
26405         resize : true,
26406         /**
26407          * @event show
26408          * Fires when the map show.
26409          * @param {Roo.bootstrap.LocationPicker} this
26410          */
26411         show : true,
26412         /**
26413          * @event hide
26414          * Fires when the map hide.
26415          * @param {Roo.bootstrap.LocationPicker} this
26416          */
26417         hide : true,
26418         /**
26419          * @event mapClick
26420          * Fires when click the map.
26421          * @param {Roo.bootstrap.LocationPicker} this
26422          * @param {Map event} e
26423          */
26424         mapClick : true,
26425         /**
26426          * @event mapRightClick
26427          * Fires when right click the map.
26428          * @param {Roo.bootstrap.LocationPicker} this
26429          * @param {Map event} e
26430          */
26431         mapRightClick : true,
26432         /**
26433          * @event markerClick
26434          * Fires when click the marker.
26435          * @param {Roo.bootstrap.LocationPicker} this
26436          * @param {Map event} e
26437          */
26438         markerClick : true,
26439         /**
26440          * @event markerRightClick
26441          * Fires when right click the marker.
26442          * @param {Roo.bootstrap.LocationPicker} this
26443          * @param {Map event} e
26444          */
26445         markerRightClick : true,
26446         /**
26447          * @event OverlayViewDraw
26448          * Fires when OverlayView Draw
26449          * @param {Roo.bootstrap.LocationPicker} this
26450          */
26451         OverlayViewDraw : true,
26452         /**
26453          * @event OverlayViewOnAdd
26454          * Fires when OverlayView Draw
26455          * @param {Roo.bootstrap.LocationPicker} this
26456          */
26457         OverlayViewOnAdd : true,
26458         /**
26459          * @event OverlayViewOnRemove
26460          * Fires when OverlayView Draw
26461          * @param {Roo.bootstrap.LocationPicker} this
26462          */
26463         OverlayViewOnRemove : true,
26464         /**
26465          * @event OverlayViewShow
26466          * Fires when OverlayView Draw
26467          * @param {Roo.bootstrap.LocationPicker} this
26468          * @param {Pixel} cpx
26469          */
26470         OverlayViewShow : true,
26471         /**
26472          * @event OverlayViewHide
26473          * Fires when OverlayView Draw
26474          * @param {Roo.bootstrap.LocationPicker} this
26475          */
26476         OverlayViewHide : true,
26477         /**
26478          * @event loadexception
26479          * Fires when load google lib failed.
26480          * @param {Roo.bootstrap.LocationPicker} this
26481          */
26482         loadexception : true
26483     });
26484         
26485 };
26486
26487 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26488     
26489     gMapContext: false,
26490     
26491     latitude: 0,
26492     longitude: 0,
26493     zoom: 15,
26494     mapTypeId: false,
26495     mapTypeControl: false,
26496     disableDoubleClickZoom: false,
26497     scrollwheel: true,
26498     streetViewControl: false,
26499     radius: 0,
26500     locationName: '',
26501     draggable: true,
26502     enableAutocomplete: false,
26503     enableReverseGeocode: true,
26504     markerTitle: '',
26505     
26506     getAutoCreate: function()
26507     {
26508
26509         var cfg = {
26510             tag: 'div',
26511             cls: 'roo-location-picker'
26512         };
26513         
26514         return cfg
26515     },
26516     
26517     initEvents: function(ct, position)
26518     {       
26519         if(!this.el.getWidth() || this.isApplied()){
26520             return;
26521         }
26522         
26523         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26524         
26525         this.initial();
26526     },
26527     
26528     initial: function()
26529     {
26530         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26531             this.fireEvent('loadexception', this);
26532             return;
26533         }
26534         
26535         if(!this.mapTypeId){
26536             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26537         }
26538         
26539         this.gMapContext = this.GMapContext();
26540         
26541         this.initOverlayView();
26542         
26543         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26544         
26545         var _this = this;
26546                 
26547         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26548             _this.setPosition(_this.gMapContext.marker.position);
26549         });
26550         
26551         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26552             _this.fireEvent('mapClick', this, event);
26553             
26554         });
26555
26556         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26557             _this.fireEvent('mapRightClick', this, event);
26558             
26559         });
26560         
26561         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26562             _this.fireEvent('markerClick', this, event);
26563             
26564         });
26565
26566         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26567             _this.fireEvent('markerRightClick', this, event);
26568             
26569         });
26570         
26571         this.setPosition(this.gMapContext.location);
26572         
26573         this.fireEvent('initial', this, this.gMapContext.location);
26574     },
26575     
26576     initOverlayView: function()
26577     {
26578         var _this = this;
26579         
26580         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26581             
26582             draw: function()
26583             {
26584                 _this.fireEvent('OverlayViewDraw', _this);
26585             },
26586             
26587             onAdd: function()
26588             {
26589                 _this.fireEvent('OverlayViewOnAdd', _this);
26590             },
26591             
26592             onRemove: function()
26593             {
26594                 _this.fireEvent('OverlayViewOnRemove', _this);
26595             },
26596             
26597             show: function(cpx)
26598             {
26599                 _this.fireEvent('OverlayViewShow', _this, cpx);
26600             },
26601             
26602             hide: function()
26603             {
26604                 _this.fireEvent('OverlayViewHide', _this);
26605             }
26606             
26607         });
26608     },
26609     
26610     fromLatLngToContainerPixel: function(event)
26611     {
26612         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26613     },
26614     
26615     isApplied: function() 
26616     {
26617         return this.getGmapContext() == false ? false : true;
26618     },
26619     
26620     getGmapContext: function() 
26621     {
26622         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26623     },
26624     
26625     GMapContext: function() 
26626     {
26627         var position = new google.maps.LatLng(this.latitude, this.longitude);
26628         
26629         var _map = new google.maps.Map(this.el.dom, {
26630             center: position,
26631             zoom: this.zoom,
26632             mapTypeId: this.mapTypeId,
26633             mapTypeControl: this.mapTypeControl,
26634             disableDoubleClickZoom: this.disableDoubleClickZoom,
26635             scrollwheel: this.scrollwheel,
26636             streetViewControl: this.streetViewControl,
26637             locationName: this.locationName,
26638             draggable: this.draggable,
26639             enableAutocomplete: this.enableAutocomplete,
26640             enableReverseGeocode: this.enableReverseGeocode
26641         });
26642         
26643         var _marker = new google.maps.Marker({
26644             position: position,
26645             map: _map,
26646             title: this.markerTitle,
26647             draggable: this.draggable
26648         });
26649         
26650         return {
26651             map: _map,
26652             marker: _marker,
26653             circle: null,
26654             location: position,
26655             radius: this.radius,
26656             locationName: this.locationName,
26657             addressComponents: {
26658                 formatted_address: null,
26659                 addressLine1: null,
26660                 addressLine2: null,
26661                 streetName: null,
26662                 streetNumber: null,
26663                 city: null,
26664                 district: null,
26665                 state: null,
26666                 stateOrProvince: null
26667             },
26668             settings: this,
26669             domContainer: this.el.dom,
26670             geodecoder: new google.maps.Geocoder()
26671         };
26672     },
26673     
26674     drawCircle: function(center, radius, options) 
26675     {
26676         if (this.gMapContext.circle != null) {
26677             this.gMapContext.circle.setMap(null);
26678         }
26679         if (radius > 0) {
26680             radius *= 1;
26681             options = Roo.apply({}, options, {
26682                 strokeColor: "#0000FF",
26683                 strokeOpacity: .35,
26684                 strokeWeight: 2,
26685                 fillColor: "#0000FF",
26686                 fillOpacity: .2
26687             });
26688             
26689             options.map = this.gMapContext.map;
26690             options.radius = radius;
26691             options.center = center;
26692             this.gMapContext.circle = new google.maps.Circle(options);
26693             return this.gMapContext.circle;
26694         }
26695         
26696         return null;
26697     },
26698     
26699     setPosition: function(location) 
26700     {
26701         this.gMapContext.location = location;
26702         this.gMapContext.marker.setPosition(location);
26703         this.gMapContext.map.panTo(location);
26704         this.drawCircle(location, this.gMapContext.radius, {});
26705         
26706         var _this = this;
26707         
26708         if (this.gMapContext.settings.enableReverseGeocode) {
26709             this.gMapContext.geodecoder.geocode({
26710                 latLng: this.gMapContext.location
26711             }, function(results, status) {
26712                 
26713                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26714                     _this.gMapContext.locationName = results[0].formatted_address;
26715                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26716                     
26717                     _this.fireEvent('positionchanged', this, location);
26718                 }
26719             });
26720             
26721             return;
26722         }
26723         
26724         this.fireEvent('positionchanged', this, location);
26725     },
26726     
26727     resize: function()
26728     {
26729         google.maps.event.trigger(this.gMapContext.map, "resize");
26730         
26731         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26732         
26733         this.fireEvent('resize', this);
26734     },
26735     
26736     setPositionByLatLng: function(latitude, longitude)
26737     {
26738         this.setPosition(new google.maps.LatLng(latitude, longitude));
26739     },
26740     
26741     getCurrentPosition: function() 
26742     {
26743         return {
26744             latitude: this.gMapContext.location.lat(),
26745             longitude: this.gMapContext.location.lng()
26746         };
26747     },
26748     
26749     getAddressName: function() 
26750     {
26751         return this.gMapContext.locationName;
26752     },
26753     
26754     getAddressComponents: function() 
26755     {
26756         return this.gMapContext.addressComponents;
26757     },
26758     
26759     address_component_from_google_geocode: function(address_components) 
26760     {
26761         var result = {};
26762         
26763         for (var i = 0; i < address_components.length; i++) {
26764             var component = address_components[i];
26765             if (component.types.indexOf("postal_code") >= 0) {
26766                 result.postalCode = component.short_name;
26767             } else if (component.types.indexOf("street_number") >= 0) {
26768                 result.streetNumber = component.short_name;
26769             } else if (component.types.indexOf("route") >= 0) {
26770                 result.streetName = component.short_name;
26771             } else if (component.types.indexOf("neighborhood") >= 0) {
26772                 result.city = component.short_name;
26773             } else if (component.types.indexOf("locality") >= 0) {
26774                 result.city = component.short_name;
26775             } else if (component.types.indexOf("sublocality") >= 0) {
26776                 result.district = component.short_name;
26777             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26778                 result.stateOrProvince = component.short_name;
26779             } else if (component.types.indexOf("country") >= 0) {
26780                 result.country = component.short_name;
26781             }
26782         }
26783         
26784         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26785         result.addressLine2 = "";
26786         return result;
26787     },
26788     
26789     setZoomLevel: function(zoom)
26790     {
26791         this.gMapContext.map.setZoom(zoom);
26792     },
26793     
26794     show: function()
26795     {
26796         if(!this.el){
26797             return;
26798         }
26799         
26800         this.el.show();
26801         
26802         this.resize();
26803         
26804         this.fireEvent('show', this);
26805     },
26806     
26807     hide: function()
26808     {
26809         if(!this.el){
26810             return;
26811         }
26812         
26813         this.el.hide();
26814         
26815         this.fireEvent('hide', this);
26816     }
26817     
26818 });
26819
26820 Roo.apply(Roo.bootstrap.LocationPicker, {
26821     
26822     OverlayView : function(map, options)
26823     {
26824         options = options || {};
26825         
26826         this.setMap(map);
26827     }
26828     
26829     
26830 });/*
26831  * - LGPL
26832  *
26833  * Alert
26834  * 
26835  */
26836
26837 /**
26838  * @class Roo.bootstrap.Alert
26839  * @extends Roo.bootstrap.Component
26840  * Bootstrap Alert class
26841  * @cfg {String} title The title of alert
26842  * @cfg {String} html The content of alert
26843  * @cfg {String} weight (  success | info | warning | danger )
26844  * @cfg {String} faicon font-awesomeicon
26845  * 
26846  * @constructor
26847  * Create a new alert
26848  * @param {Object} config The config object
26849  */
26850
26851
26852 Roo.bootstrap.Alert = function(config){
26853     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26854     
26855 };
26856
26857 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26858     
26859     title: '',
26860     html: '',
26861     weight: false,
26862     faicon: false,
26863     
26864     getAutoCreate : function()
26865     {
26866         
26867         var cfg = {
26868             tag : 'div',
26869             cls : 'alert',
26870             cn : [
26871                 {
26872                     tag : 'i',
26873                     cls : 'roo-alert-icon'
26874                     
26875                 },
26876                 {
26877                     tag : 'b',
26878                     cls : 'roo-alert-title',
26879                     html : this.title
26880                 },
26881                 {
26882                     tag : 'span',
26883                     cls : 'roo-alert-text',
26884                     html : this.html
26885                 }
26886             ]
26887         };
26888         
26889         if(this.faicon){
26890             cfg.cn[0].cls += ' fa ' + this.faicon;
26891         }
26892         
26893         if(this.weight){
26894             cfg.cls += ' alert-' + this.weight;
26895         }
26896         
26897         return cfg;
26898     },
26899     
26900     initEvents: function() 
26901     {
26902         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26903     },
26904     
26905     setTitle : function(str)
26906     {
26907         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26908     },
26909     
26910     setText : function(str)
26911     {
26912         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26913     },
26914     
26915     setWeight : function(weight)
26916     {
26917         if(this.weight){
26918             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26919         }
26920         
26921         this.weight = weight;
26922         
26923         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26924     },
26925     
26926     setIcon : function(icon)
26927     {
26928         if(this.faicon){
26929             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26930         }
26931         
26932         this.faicon = icon;
26933         
26934         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26935     },
26936     
26937     hide: function() 
26938     {
26939         this.el.hide();   
26940     },
26941     
26942     show: function() 
26943     {  
26944         this.el.show();   
26945     }
26946     
26947 });
26948
26949  
26950 /*
26951 * Licence: LGPL
26952 */
26953
26954 /**
26955  * @class Roo.bootstrap.UploadCropbox
26956  * @extends Roo.bootstrap.Component
26957  * Bootstrap UploadCropbox class
26958  * @cfg {String} emptyText show when image has been loaded
26959  * @cfg {String} rotateNotify show when image too small to rotate
26960  * @cfg {Number} errorTimeout default 3000
26961  * @cfg {Number} minWidth default 300
26962  * @cfg {Number} minHeight default 300
26963  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26964  * @cfg {Boolean} isDocument (true|false) default false
26965  * @cfg {String} url action url
26966  * @cfg {String} paramName default 'imageUpload'
26967  * @cfg {String} method default POST
26968  * @cfg {Boolean} loadMask (true|false) default true
26969  * @cfg {Boolean} loadingText default 'Loading...'
26970  * 
26971  * @constructor
26972  * Create a new UploadCropbox
26973  * @param {Object} config The config object
26974  */
26975
26976 Roo.bootstrap.UploadCropbox = function(config){
26977     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26978     
26979     this.addEvents({
26980         /**
26981          * @event beforeselectfile
26982          * Fire before select file
26983          * @param {Roo.bootstrap.UploadCropbox} this
26984          */
26985         "beforeselectfile" : true,
26986         /**
26987          * @event initial
26988          * Fire after initEvent
26989          * @param {Roo.bootstrap.UploadCropbox} this
26990          */
26991         "initial" : true,
26992         /**
26993          * @event crop
26994          * Fire after initEvent
26995          * @param {Roo.bootstrap.UploadCropbox} this
26996          * @param {String} data
26997          */
26998         "crop" : true,
26999         /**
27000          * @event prepare
27001          * Fire when preparing the file data
27002          * @param {Roo.bootstrap.UploadCropbox} this
27003          * @param {Object} file
27004          */
27005         "prepare" : true,
27006         /**
27007          * @event exception
27008          * Fire when get exception
27009          * @param {Roo.bootstrap.UploadCropbox} this
27010          * @param {XMLHttpRequest} xhr
27011          */
27012         "exception" : true,
27013         /**
27014          * @event beforeloadcanvas
27015          * Fire before load the canvas
27016          * @param {Roo.bootstrap.UploadCropbox} this
27017          * @param {String} src
27018          */
27019         "beforeloadcanvas" : true,
27020         /**
27021          * @event trash
27022          * Fire when trash image
27023          * @param {Roo.bootstrap.UploadCropbox} this
27024          */
27025         "trash" : true,
27026         /**
27027          * @event download
27028          * Fire when download the image
27029          * @param {Roo.bootstrap.UploadCropbox} this
27030          */
27031         "download" : true,
27032         /**
27033          * @event footerbuttonclick
27034          * Fire when footerbuttonclick
27035          * @param {Roo.bootstrap.UploadCropbox} this
27036          * @param {String} type
27037          */
27038         "footerbuttonclick" : true,
27039         /**
27040          * @event resize
27041          * Fire when resize
27042          * @param {Roo.bootstrap.UploadCropbox} this
27043          */
27044         "resize" : true,
27045         /**
27046          * @event rotate
27047          * Fire when rotate the image
27048          * @param {Roo.bootstrap.UploadCropbox} this
27049          * @param {String} pos
27050          */
27051         "rotate" : true,
27052         /**
27053          * @event inspect
27054          * Fire when inspect the file
27055          * @param {Roo.bootstrap.UploadCropbox} this
27056          * @param {Object} file
27057          */
27058         "inspect" : true,
27059         /**
27060          * @event upload
27061          * Fire when xhr upload the file
27062          * @param {Roo.bootstrap.UploadCropbox} this
27063          * @param {Object} data
27064          */
27065         "upload" : true,
27066         /**
27067          * @event arrange
27068          * Fire when arrange the file data
27069          * @param {Roo.bootstrap.UploadCropbox} this
27070          * @param {Object} formData
27071          */
27072         "arrange" : true
27073     });
27074     
27075     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27076 };
27077
27078 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27079     
27080     emptyText : 'Click to upload image',
27081     rotateNotify : 'Image is too small to rotate',
27082     errorTimeout : 3000,
27083     scale : 0,
27084     baseScale : 1,
27085     rotate : 0,
27086     dragable : false,
27087     pinching : false,
27088     mouseX : 0,
27089     mouseY : 0,
27090     cropData : false,
27091     minWidth : 300,
27092     minHeight : 300,
27093     file : false,
27094     exif : {},
27095     baseRotate : 1,
27096     cropType : 'image/jpeg',
27097     buttons : false,
27098     canvasLoaded : false,
27099     isDocument : false,
27100     method : 'POST',
27101     paramName : 'imageUpload',
27102     loadMask : true,
27103     loadingText : 'Loading...',
27104     maskEl : false,
27105     
27106     getAutoCreate : function()
27107     {
27108         var cfg = {
27109             tag : 'div',
27110             cls : 'roo-upload-cropbox',
27111             cn : [
27112                 {
27113                     tag : 'input',
27114                     cls : 'roo-upload-cropbox-selector',
27115                     type : 'file'
27116                 },
27117                 {
27118                     tag : 'div',
27119                     cls : 'roo-upload-cropbox-body',
27120                     style : 'cursor:pointer',
27121                     cn : [
27122                         {
27123                             tag : 'div',
27124                             cls : 'roo-upload-cropbox-preview'
27125                         },
27126                         {
27127                             tag : 'div',
27128                             cls : 'roo-upload-cropbox-thumb'
27129                         },
27130                         {
27131                             tag : 'div',
27132                             cls : 'roo-upload-cropbox-empty-notify',
27133                             html : this.emptyText
27134                         },
27135                         {
27136                             tag : 'div',
27137                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27138                             html : this.rotateNotify
27139                         }
27140                     ]
27141                 },
27142                 {
27143                     tag : 'div',
27144                     cls : 'roo-upload-cropbox-footer',
27145                     cn : {
27146                         tag : 'div',
27147                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27148                         cn : []
27149                     }
27150                 }
27151             ]
27152         };
27153         
27154         return cfg;
27155     },
27156     
27157     onRender : function(ct, position)
27158     {
27159         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27160         
27161         if (this.buttons.length) {
27162             
27163             Roo.each(this.buttons, function(bb) {
27164                 
27165                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27166                 
27167                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27168                 
27169             }, this);
27170         }
27171         
27172         if(this.loadMask){
27173             this.maskEl = this.el;
27174         }
27175     },
27176     
27177     initEvents : function()
27178     {
27179         this.urlAPI = (window.createObjectURL && window) || 
27180                                 (window.URL && URL.revokeObjectURL && URL) || 
27181                                 (window.webkitURL && webkitURL);
27182                         
27183         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27184         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27185         
27186         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27187         this.selectorEl.hide();
27188         
27189         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27190         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27191         
27192         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27193         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27194         this.thumbEl.hide();
27195         
27196         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27197         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27198         
27199         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27200         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27201         this.errorEl.hide();
27202         
27203         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27204         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27205         this.footerEl.hide();
27206         
27207         this.setThumbBoxSize();
27208         
27209         this.bind();
27210         
27211         this.resize();
27212         
27213         this.fireEvent('initial', this);
27214     },
27215
27216     bind : function()
27217     {
27218         var _this = this;
27219         
27220         window.addEventListener("resize", function() { _this.resize(); } );
27221         
27222         this.bodyEl.on('click', this.beforeSelectFile, this);
27223         
27224         if(Roo.isTouch){
27225             this.bodyEl.on('touchstart', this.onTouchStart, this);
27226             this.bodyEl.on('touchmove', this.onTouchMove, this);
27227             this.bodyEl.on('touchend', this.onTouchEnd, this);
27228         }
27229         
27230         if(!Roo.isTouch){
27231             this.bodyEl.on('mousedown', this.onMouseDown, this);
27232             this.bodyEl.on('mousemove', this.onMouseMove, this);
27233             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27234             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27235             Roo.get(document).on('mouseup', this.onMouseUp, this);
27236         }
27237         
27238         this.selectorEl.on('change', this.onFileSelected, this);
27239     },
27240     
27241     reset : function()
27242     {    
27243         this.scale = 0;
27244         this.baseScale = 1;
27245         this.rotate = 0;
27246         this.baseRotate = 1;
27247         this.dragable = false;
27248         this.pinching = false;
27249         this.mouseX = 0;
27250         this.mouseY = 0;
27251         this.cropData = false;
27252         this.notifyEl.dom.innerHTML = this.emptyText;
27253         
27254         this.selectorEl.dom.value = '';
27255         
27256     },
27257     
27258     resize : function()
27259     {
27260         if(this.fireEvent('resize', this) != false){
27261             this.setThumbBoxPosition();
27262             this.setCanvasPosition();
27263         }
27264     },
27265     
27266     onFooterButtonClick : function(e, el, o, type)
27267     {
27268         switch (type) {
27269             case 'rotate-left' :
27270                 this.onRotateLeft(e);
27271                 break;
27272             case 'rotate-right' :
27273                 this.onRotateRight(e);
27274                 break;
27275             case 'picture' :
27276                 this.beforeSelectFile(e);
27277                 break;
27278             case 'trash' :
27279                 this.trash(e);
27280                 break;
27281             case 'crop' :
27282                 this.crop(e);
27283                 break;
27284             case 'download' :
27285                 this.download(e);
27286                 break;
27287             default :
27288                 break;
27289         }
27290         
27291         this.fireEvent('footerbuttonclick', this, type);
27292     },
27293     
27294     beforeSelectFile : function(e)
27295     {
27296         e.preventDefault();
27297         
27298         if(this.fireEvent('beforeselectfile', this) != false){
27299             this.selectorEl.dom.click();
27300         }
27301     },
27302     
27303     onFileSelected : function(e)
27304     {
27305         e.preventDefault();
27306         
27307         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27308             return;
27309         }
27310         
27311         var file = this.selectorEl.dom.files[0];
27312         
27313         if(this.fireEvent('inspect', this, file) != false){
27314             this.prepare(file);
27315         }
27316         
27317     },
27318     
27319     trash : function(e)
27320     {
27321         this.fireEvent('trash', this);
27322     },
27323     
27324     download : function(e)
27325     {
27326         this.fireEvent('download', this);
27327     },
27328     
27329     loadCanvas : function(src)
27330     {   
27331         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27332             
27333             this.reset();
27334             
27335             this.imageEl = document.createElement('img');
27336             
27337             var _this = this;
27338             
27339             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27340             
27341             this.imageEl.src = src;
27342         }
27343     },
27344     
27345     onLoadCanvas : function()
27346     {   
27347         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27348         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27349         
27350         this.bodyEl.un('click', this.beforeSelectFile, this);
27351         
27352         this.notifyEl.hide();
27353         this.thumbEl.show();
27354         this.footerEl.show();
27355         
27356         this.baseRotateLevel();
27357         
27358         if(this.isDocument){
27359             this.setThumbBoxSize();
27360         }
27361         
27362         this.setThumbBoxPosition();
27363         
27364         this.baseScaleLevel();
27365         
27366         this.draw();
27367         
27368         this.resize();
27369         
27370         this.canvasLoaded = true;
27371         
27372         if(this.loadMask){
27373             this.maskEl.unmask();
27374         }
27375         
27376     },
27377     
27378     setCanvasPosition : function()
27379     {   
27380         if(!this.canvasEl){
27381             return;
27382         }
27383         
27384         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27385         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27386         
27387         this.previewEl.setLeft(pw);
27388         this.previewEl.setTop(ph);
27389         
27390     },
27391     
27392     onMouseDown : function(e)
27393     {   
27394         e.stopEvent();
27395         
27396         this.dragable = true;
27397         this.pinching = false;
27398         
27399         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27400             this.dragable = false;
27401             return;
27402         }
27403         
27404         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27405         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27406         
27407     },
27408     
27409     onMouseMove : function(e)
27410     {   
27411         e.stopEvent();
27412         
27413         if(!this.canvasLoaded){
27414             return;
27415         }
27416         
27417         if (!this.dragable){
27418             return;
27419         }
27420         
27421         var minX = Math.ceil(this.thumbEl.getLeft(true));
27422         var minY = Math.ceil(this.thumbEl.getTop(true));
27423         
27424         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27425         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27426         
27427         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27428         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27429         
27430         x = x - this.mouseX;
27431         y = y - this.mouseY;
27432         
27433         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27434         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27435         
27436         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27437         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27438         
27439         this.previewEl.setLeft(bgX);
27440         this.previewEl.setTop(bgY);
27441         
27442         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27443         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27444     },
27445     
27446     onMouseUp : function(e)
27447     {   
27448         e.stopEvent();
27449         
27450         this.dragable = false;
27451     },
27452     
27453     onMouseWheel : function(e)
27454     {   
27455         e.stopEvent();
27456         
27457         this.startScale = this.scale;
27458         
27459         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27460         
27461         if(!this.zoomable()){
27462             this.scale = this.startScale;
27463             return;
27464         }
27465         
27466         this.draw();
27467         
27468         return;
27469     },
27470     
27471     zoomable : function()
27472     {
27473         var minScale = this.thumbEl.getWidth() / this.minWidth;
27474         
27475         if(this.minWidth < this.minHeight){
27476             minScale = this.thumbEl.getHeight() / this.minHeight;
27477         }
27478         
27479         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27480         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27481         
27482         if(
27483                 this.isDocument &&
27484                 (this.rotate == 0 || this.rotate == 180) && 
27485                 (
27486                     width > this.imageEl.OriginWidth || 
27487                     height > this.imageEl.OriginHeight ||
27488                     (width < this.minWidth && height < this.minHeight)
27489                 )
27490         ){
27491             return false;
27492         }
27493         
27494         if(
27495                 this.isDocument &&
27496                 (this.rotate == 90 || this.rotate == 270) && 
27497                 (
27498                     width > this.imageEl.OriginWidth || 
27499                     height > this.imageEl.OriginHeight ||
27500                     (width < this.minHeight && height < this.minWidth)
27501                 )
27502         ){
27503             return false;
27504         }
27505         
27506         if(
27507                 !this.isDocument &&
27508                 (this.rotate == 0 || this.rotate == 180) && 
27509                 (
27510                     width < this.minWidth || 
27511                     width > this.imageEl.OriginWidth || 
27512                     height < this.minHeight || 
27513                     height > this.imageEl.OriginHeight
27514                 )
27515         ){
27516             return false;
27517         }
27518         
27519         if(
27520                 !this.isDocument &&
27521                 (this.rotate == 90 || this.rotate == 270) && 
27522                 (
27523                     width < this.minHeight || 
27524                     width > this.imageEl.OriginWidth || 
27525                     height < this.minWidth || 
27526                     height > this.imageEl.OriginHeight
27527                 )
27528         ){
27529             return false;
27530         }
27531         
27532         return true;
27533         
27534     },
27535     
27536     onRotateLeft : function(e)
27537     {   
27538         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27539             
27540             var minScale = this.thumbEl.getWidth() / this.minWidth;
27541             
27542             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27543             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27544             
27545             this.startScale = this.scale;
27546             
27547             while (this.getScaleLevel() < minScale){
27548             
27549                 this.scale = this.scale + 1;
27550                 
27551                 if(!this.zoomable()){
27552                     break;
27553                 }
27554                 
27555                 if(
27556                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27557                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27558                 ){
27559                     continue;
27560                 }
27561                 
27562                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27563
27564                 this.draw();
27565                 
27566                 return;
27567             }
27568             
27569             this.scale = this.startScale;
27570             
27571             this.onRotateFail();
27572             
27573             return false;
27574         }
27575         
27576         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27577
27578         if(this.isDocument){
27579             this.setThumbBoxSize();
27580             this.setThumbBoxPosition();
27581             this.setCanvasPosition();
27582         }
27583         
27584         this.draw();
27585         
27586         this.fireEvent('rotate', this, 'left');
27587         
27588     },
27589     
27590     onRotateRight : function(e)
27591     {
27592         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27593             
27594             var minScale = this.thumbEl.getWidth() / this.minWidth;
27595         
27596             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27597             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27598             
27599             this.startScale = this.scale;
27600             
27601             while (this.getScaleLevel() < minScale){
27602             
27603                 this.scale = this.scale + 1;
27604                 
27605                 if(!this.zoomable()){
27606                     break;
27607                 }
27608                 
27609                 if(
27610                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27611                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27612                 ){
27613                     continue;
27614                 }
27615                 
27616                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27617
27618                 this.draw();
27619                 
27620                 return;
27621             }
27622             
27623             this.scale = this.startScale;
27624             
27625             this.onRotateFail();
27626             
27627             return false;
27628         }
27629         
27630         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27631
27632         if(this.isDocument){
27633             this.setThumbBoxSize();
27634             this.setThumbBoxPosition();
27635             this.setCanvasPosition();
27636         }
27637         
27638         this.draw();
27639         
27640         this.fireEvent('rotate', this, 'right');
27641     },
27642     
27643     onRotateFail : function()
27644     {
27645         this.errorEl.show(true);
27646         
27647         var _this = this;
27648         
27649         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27650     },
27651     
27652     draw : function()
27653     {
27654         this.previewEl.dom.innerHTML = '';
27655         
27656         var canvasEl = document.createElement("canvas");
27657         
27658         var contextEl = canvasEl.getContext("2d");
27659         
27660         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27661         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27662         var center = this.imageEl.OriginWidth / 2;
27663         
27664         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27665             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27666             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27667             center = this.imageEl.OriginHeight / 2;
27668         }
27669         
27670         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27671         
27672         contextEl.translate(center, center);
27673         contextEl.rotate(this.rotate * Math.PI / 180);
27674
27675         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27676         
27677         this.canvasEl = document.createElement("canvas");
27678         
27679         this.contextEl = this.canvasEl.getContext("2d");
27680         
27681         switch (this.rotate) {
27682             case 0 :
27683                 
27684                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27685                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27686                 
27687                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27688                 
27689                 break;
27690             case 90 : 
27691                 
27692                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27693                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27694                 
27695                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27696                     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);
27697                     break;
27698                 }
27699                 
27700                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27701                 
27702                 break;
27703             case 180 :
27704                 
27705                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27706                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27707                 
27708                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27709                     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);
27710                     break;
27711                 }
27712                 
27713                 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);
27714                 
27715                 break;
27716             case 270 :
27717                 
27718                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27719                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27720         
27721                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27722                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27723                     break;
27724                 }
27725                 
27726                 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);
27727                 
27728                 break;
27729             default : 
27730                 break;
27731         }
27732         
27733         this.previewEl.appendChild(this.canvasEl);
27734         
27735         this.setCanvasPosition();
27736     },
27737     
27738     crop : function()
27739     {
27740         if(!this.canvasLoaded){
27741             return;
27742         }
27743         
27744         var imageCanvas = document.createElement("canvas");
27745         
27746         var imageContext = imageCanvas.getContext("2d");
27747         
27748         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27749         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27750         
27751         var center = imageCanvas.width / 2;
27752         
27753         imageContext.translate(center, center);
27754         
27755         imageContext.rotate(this.rotate * Math.PI / 180);
27756         
27757         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27758         
27759         var canvas = document.createElement("canvas");
27760         
27761         var context = canvas.getContext("2d");
27762                 
27763         canvas.width = this.minWidth;
27764         canvas.height = this.minHeight;
27765
27766         switch (this.rotate) {
27767             case 0 :
27768                 
27769                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27770                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27771                 
27772                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27773                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27774                 
27775                 var targetWidth = this.minWidth - 2 * x;
27776                 var targetHeight = this.minHeight - 2 * y;
27777                 
27778                 var scale = 1;
27779                 
27780                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27781                     scale = targetWidth / width;
27782                 }
27783                 
27784                 if(x > 0 && y == 0){
27785                     scale = targetHeight / height;
27786                 }
27787                 
27788                 if(x > 0 && y > 0){
27789                     scale = targetWidth / width;
27790                     
27791                     if(width < height){
27792                         scale = targetHeight / height;
27793                     }
27794                 }
27795                 
27796                 context.scale(scale, scale);
27797                 
27798                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27799                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27800
27801                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27802                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27803
27804                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27805                 
27806                 break;
27807             case 90 : 
27808                 
27809                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27810                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27811                 
27812                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27813                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27814                 
27815                 var targetWidth = this.minWidth - 2 * x;
27816                 var targetHeight = this.minHeight - 2 * y;
27817                 
27818                 var scale = 1;
27819                 
27820                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27821                     scale = targetWidth / width;
27822                 }
27823                 
27824                 if(x > 0 && y == 0){
27825                     scale = targetHeight / height;
27826                 }
27827                 
27828                 if(x > 0 && y > 0){
27829                     scale = targetWidth / width;
27830                     
27831                     if(width < height){
27832                         scale = targetHeight / height;
27833                     }
27834                 }
27835                 
27836                 context.scale(scale, scale);
27837                 
27838                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27839                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27840
27841                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27842                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27843                 
27844                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27845                 
27846                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27847                 
27848                 break;
27849             case 180 :
27850                 
27851                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27852                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27853                 
27854                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27855                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27856                 
27857                 var targetWidth = this.minWidth - 2 * x;
27858                 var targetHeight = this.minHeight - 2 * y;
27859                 
27860                 var scale = 1;
27861                 
27862                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27863                     scale = targetWidth / width;
27864                 }
27865                 
27866                 if(x > 0 && y == 0){
27867                     scale = targetHeight / height;
27868                 }
27869                 
27870                 if(x > 0 && y > 0){
27871                     scale = targetWidth / width;
27872                     
27873                     if(width < height){
27874                         scale = targetHeight / height;
27875                     }
27876                 }
27877                 
27878                 context.scale(scale, scale);
27879                 
27880                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27881                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27882
27883                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27884                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27885
27886                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27887                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27888                 
27889                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27890                 
27891                 break;
27892             case 270 :
27893                 
27894                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27895                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27896                 
27897                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27898                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27899                 
27900                 var targetWidth = this.minWidth - 2 * x;
27901                 var targetHeight = this.minHeight - 2 * y;
27902                 
27903                 var scale = 1;
27904                 
27905                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27906                     scale = targetWidth / width;
27907                 }
27908                 
27909                 if(x > 0 && y == 0){
27910                     scale = targetHeight / height;
27911                 }
27912                 
27913                 if(x > 0 && y > 0){
27914                     scale = targetWidth / width;
27915                     
27916                     if(width < height){
27917                         scale = targetHeight / height;
27918                     }
27919                 }
27920                 
27921                 context.scale(scale, scale);
27922                 
27923                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27924                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27925
27926                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27927                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27928                 
27929                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27930                 
27931                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27932                 
27933                 break;
27934             default : 
27935                 break;
27936         }
27937         
27938         this.cropData = canvas.toDataURL(this.cropType);
27939         
27940         if(this.fireEvent('crop', this, this.cropData) !== false){
27941             this.process(this.file, this.cropData);
27942         }
27943         
27944         return;
27945         
27946     },
27947     
27948     setThumbBoxSize : function()
27949     {
27950         var width, height;
27951         
27952         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27953             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27954             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27955             
27956             this.minWidth = width;
27957             this.minHeight = height;
27958             
27959             if(this.rotate == 90 || this.rotate == 270){
27960                 this.minWidth = height;
27961                 this.minHeight = width;
27962             }
27963         }
27964         
27965         height = 300;
27966         width = Math.ceil(this.minWidth * height / this.minHeight);
27967         
27968         if(this.minWidth > this.minHeight){
27969             width = 300;
27970             height = Math.ceil(this.minHeight * width / this.minWidth);
27971         }
27972         
27973         this.thumbEl.setStyle({
27974             width : width + 'px',
27975             height : height + 'px'
27976         });
27977
27978         return;
27979             
27980     },
27981     
27982     setThumbBoxPosition : function()
27983     {
27984         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27985         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27986         
27987         this.thumbEl.setLeft(x);
27988         this.thumbEl.setTop(y);
27989         
27990     },
27991     
27992     baseRotateLevel : function()
27993     {
27994         this.baseRotate = 1;
27995         
27996         if(
27997                 typeof(this.exif) != 'undefined' &&
27998                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27999                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28000         ){
28001             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28002         }
28003         
28004         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28005         
28006     },
28007     
28008     baseScaleLevel : function()
28009     {
28010         var width, height;
28011         
28012         if(this.isDocument){
28013             
28014             if(this.baseRotate == 6 || this.baseRotate == 8){
28015             
28016                 height = this.thumbEl.getHeight();
28017                 this.baseScale = height / this.imageEl.OriginWidth;
28018
28019                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28020                     width = this.thumbEl.getWidth();
28021                     this.baseScale = width / this.imageEl.OriginHeight;
28022                 }
28023
28024                 return;
28025             }
28026
28027             height = this.thumbEl.getHeight();
28028             this.baseScale = height / this.imageEl.OriginHeight;
28029
28030             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28031                 width = this.thumbEl.getWidth();
28032                 this.baseScale = width / this.imageEl.OriginWidth;
28033             }
28034
28035             return;
28036         }
28037         
28038         if(this.baseRotate == 6 || this.baseRotate == 8){
28039             
28040             width = this.thumbEl.getHeight();
28041             this.baseScale = width / this.imageEl.OriginHeight;
28042             
28043             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28044                 height = this.thumbEl.getWidth();
28045                 this.baseScale = height / this.imageEl.OriginHeight;
28046             }
28047             
28048             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28049                 height = this.thumbEl.getWidth();
28050                 this.baseScale = height / this.imageEl.OriginHeight;
28051                 
28052                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28053                     width = this.thumbEl.getHeight();
28054                     this.baseScale = width / this.imageEl.OriginWidth;
28055                 }
28056             }
28057             
28058             return;
28059         }
28060         
28061         width = this.thumbEl.getWidth();
28062         this.baseScale = width / this.imageEl.OriginWidth;
28063         
28064         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28065             height = this.thumbEl.getHeight();
28066             this.baseScale = height / this.imageEl.OriginHeight;
28067         }
28068         
28069         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28070             
28071             height = this.thumbEl.getHeight();
28072             this.baseScale = height / this.imageEl.OriginHeight;
28073             
28074             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28075                 width = this.thumbEl.getWidth();
28076                 this.baseScale = width / this.imageEl.OriginWidth;
28077             }
28078             
28079         }
28080         
28081         return;
28082     },
28083     
28084     getScaleLevel : function()
28085     {
28086         return this.baseScale * Math.pow(1.1, this.scale);
28087     },
28088     
28089     onTouchStart : function(e)
28090     {
28091         if(!this.canvasLoaded){
28092             this.beforeSelectFile(e);
28093             return;
28094         }
28095         
28096         var touches = e.browserEvent.touches;
28097         
28098         if(!touches){
28099             return;
28100         }
28101         
28102         if(touches.length == 1){
28103             this.onMouseDown(e);
28104             return;
28105         }
28106         
28107         if(touches.length != 2){
28108             return;
28109         }
28110         
28111         var coords = [];
28112         
28113         for(var i = 0, finger; finger = touches[i]; i++){
28114             coords.push(finger.pageX, finger.pageY);
28115         }
28116         
28117         var x = Math.pow(coords[0] - coords[2], 2);
28118         var y = Math.pow(coords[1] - coords[3], 2);
28119         
28120         this.startDistance = Math.sqrt(x + y);
28121         
28122         this.startScale = this.scale;
28123         
28124         this.pinching = true;
28125         this.dragable = false;
28126         
28127     },
28128     
28129     onTouchMove : function(e)
28130     {
28131         if(!this.pinching && !this.dragable){
28132             return;
28133         }
28134         
28135         var touches = e.browserEvent.touches;
28136         
28137         if(!touches){
28138             return;
28139         }
28140         
28141         if(this.dragable){
28142             this.onMouseMove(e);
28143             return;
28144         }
28145         
28146         var coords = [];
28147         
28148         for(var i = 0, finger; finger = touches[i]; i++){
28149             coords.push(finger.pageX, finger.pageY);
28150         }
28151         
28152         var x = Math.pow(coords[0] - coords[2], 2);
28153         var y = Math.pow(coords[1] - coords[3], 2);
28154         
28155         this.endDistance = Math.sqrt(x + y);
28156         
28157         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28158         
28159         if(!this.zoomable()){
28160             this.scale = this.startScale;
28161             return;
28162         }
28163         
28164         this.draw();
28165         
28166     },
28167     
28168     onTouchEnd : function(e)
28169     {
28170         this.pinching = false;
28171         this.dragable = false;
28172         
28173     },
28174     
28175     process : function(file, crop)
28176     {
28177         if(this.loadMask){
28178             this.maskEl.mask(this.loadingText);
28179         }
28180         
28181         this.xhr = new XMLHttpRequest();
28182         
28183         file.xhr = this.xhr;
28184
28185         this.xhr.open(this.method, this.url, true);
28186         
28187         var headers = {
28188             "Accept": "application/json",
28189             "Cache-Control": "no-cache",
28190             "X-Requested-With": "XMLHttpRequest"
28191         };
28192         
28193         for (var headerName in headers) {
28194             var headerValue = headers[headerName];
28195             if (headerValue) {
28196                 this.xhr.setRequestHeader(headerName, headerValue);
28197             }
28198         }
28199         
28200         var _this = this;
28201         
28202         this.xhr.onload = function()
28203         {
28204             _this.xhrOnLoad(_this.xhr);
28205         }
28206         
28207         this.xhr.onerror = function()
28208         {
28209             _this.xhrOnError(_this.xhr);
28210         }
28211         
28212         var formData = new FormData();
28213
28214         formData.append('returnHTML', 'NO');
28215         
28216         if(crop){
28217             formData.append('crop', crop);
28218         }
28219         
28220         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28221             formData.append(this.paramName, file, file.name);
28222         }
28223         
28224         if(typeof(file.filename) != 'undefined'){
28225             formData.append('filename', file.filename);
28226         }
28227         
28228         if(typeof(file.mimetype) != 'undefined'){
28229             formData.append('mimetype', file.mimetype);
28230         }
28231         
28232         if(this.fireEvent('arrange', this, formData) != false){
28233             this.xhr.send(formData);
28234         };
28235     },
28236     
28237     xhrOnLoad : function(xhr)
28238     {
28239         if(this.loadMask){
28240             this.maskEl.unmask();
28241         }
28242         
28243         if (xhr.readyState !== 4) {
28244             this.fireEvent('exception', this, xhr);
28245             return;
28246         }
28247
28248         var response = Roo.decode(xhr.responseText);
28249         
28250         if(!response.success){
28251             this.fireEvent('exception', this, xhr);
28252             return;
28253         }
28254         
28255         var response = Roo.decode(xhr.responseText);
28256         
28257         this.fireEvent('upload', this, response);
28258         
28259     },
28260     
28261     xhrOnError : function()
28262     {
28263         if(this.loadMask){
28264             this.maskEl.unmask();
28265         }
28266         
28267         Roo.log('xhr on error');
28268         
28269         var response = Roo.decode(xhr.responseText);
28270           
28271         Roo.log(response);
28272         
28273     },
28274     
28275     prepare : function(file)
28276     {   
28277         if(this.loadMask){
28278             this.maskEl.mask(this.loadingText);
28279         }
28280         
28281         this.file = false;
28282         this.exif = {};
28283         
28284         if(typeof(file) === 'string'){
28285             this.loadCanvas(file);
28286             return;
28287         }
28288         
28289         if(!file || !this.urlAPI){
28290             return;
28291         }
28292         
28293         this.file = file;
28294         this.cropType = file.type;
28295         
28296         var _this = this;
28297         
28298         if(this.fireEvent('prepare', this, this.file) != false){
28299             
28300             var reader = new FileReader();
28301             
28302             reader.onload = function (e) {
28303                 if (e.target.error) {
28304                     Roo.log(e.target.error);
28305                     return;
28306                 }
28307                 
28308                 var buffer = e.target.result,
28309                     dataView = new DataView(buffer),
28310                     offset = 2,
28311                     maxOffset = dataView.byteLength - 4,
28312                     markerBytes,
28313                     markerLength;
28314                 
28315                 if (dataView.getUint16(0) === 0xffd8) {
28316                     while (offset < maxOffset) {
28317                         markerBytes = dataView.getUint16(offset);
28318                         
28319                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28320                             markerLength = dataView.getUint16(offset + 2) + 2;
28321                             if (offset + markerLength > dataView.byteLength) {
28322                                 Roo.log('Invalid meta data: Invalid segment size.');
28323                                 break;
28324                             }
28325                             
28326                             if(markerBytes == 0xffe1){
28327                                 _this.parseExifData(
28328                                     dataView,
28329                                     offset,
28330                                     markerLength
28331                                 );
28332                             }
28333                             
28334                             offset += markerLength;
28335                             
28336                             continue;
28337                         }
28338                         
28339                         break;
28340                     }
28341                     
28342                 }
28343                 
28344                 var url = _this.urlAPI.createObjectURL(_this.file);
28345                 
28346                 _this.loadCanvas(url);
28347                 
28348                 return;
28349             }
28350             
28351             reader.readAsArrayBuffer(this.file);
28352             
28353         }
28354         
28355     },
28356     
28357     parseExifData : function(dataView, offset, length)
28358     {
28359         var tiffOffset = offset + 10,
28360             littleEndian,
28361             dirOffset;
28362     
28363         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28364             // No Exif data, might be XMP data instead
28365             return;
28366         }
28367         
28368         // Check for the ASCII code for "Exif" (0x45786966):
28369         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28370             // No Exif data, might be XMP data instead
28371             return;
28372         }
28373         if (tiffOffset + 8 > dataView.byteLength) {
28374             Roo.log('Invalid Exif data: Invalid segment size.');
28375             return;
28376         }
28377         // Check for the two null bytes:
28378         if (dataView.getUint16(offset + 8) !== 0x0000) {
28379             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28380             return;
28381         }
28382         // Check the byte alignment:
28383         switch (dataView.getUint16(tiffOffset)) {
28384         case 0x4949:
28385             littleEndian = true;
28386             break;
28387         case 0x4D4D:
28388             littleEndian = false;
28389             break;
28390         default:
28391             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28392             return;
28393         }
28394         // Check for the TIFF tag marker (0x002A):
28395         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28396             Roo.log('Invalid Exif data: Missing TIFF marker.');
28397             return;
28398         }
28399         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28400         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28401         
28402         this.parseExifTags(
28403             dataView,
28404             tiffOffset,
28405             tiffOffset + dirOffset,
28406             littleEndian
28407         );
28408     },
28409     
28410     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28411     {
28412         var tagsNumber,
28413             dirEndOffset,
28414             i;
28415         if (dirOffset + 6 > dataView.byteLength) {
28416             Roo.log('Invalid Exif data: Invalid directory offset.');
28417             return;
28418         }
28419         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28420         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28421         if (dirEndOffset + 4 > dataView.byteLength) {
28422             Roo.log('Invalid Exif data: Invalid directory size.');
28423             return;
28424         }
28425         for (i = 0; i < tagsNumber; i += 1) {
28426             this.parseExifTag(
28427                 dataView,
28428                 tiffOffset,
28429                 dirOffset + 2 + 12 * i, // tag offset
28430                 littleEndian
28431             );
28432         }
28433         // Return the offset to the next directory:
28434         return dataView.getUint32(dirEndOffset, littleEndian);
28435     },
28436     
28437     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28438     {
28439         var tag = dataView.getUint16(offset, littleEndian);
28440         
28441         this.exif[tag] = this.getExifValue(
28442             dataView,
28443             tiffOffset,
28444             offset,
28445             dataView.getUint16(offset + 2, littleEndian), // tag type
28446             dataView.getUint32(offset + 4, littleEndian), // tag length
28447             littleEndian
28448         );
28449     },
28450     
28451     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28452     {
28453         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28454             tagSize,
28455             dataOffset,
28456             values,
28457             i,
28458             str,
28459             c;
28460     
28461         if (!tagType) {
28462             Roo.log('Invalid Exif data: Invalid tag type.');
28463             return;
28464         }
28465         
28466         tagSize = tagType.size * length;
28467         // Determine if the value is contained in the dataOffset bytes,
28468         // or if the value at the dataOffset is a pointer to the actual data:
28469         dataOffset = tagSize > 4 ?
28470                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28471         if (dataOffset + tagSize > dataView.byteLength) {
28472             Roo.log('Invalid Exif data: Invalid data offset.');
28473             return;
28474         }
28475         if (length === 1) {
28476             return tagType.getValue(dataView, dataOffset, littleEndian);
28477         }
28478         values = [];
28479         for (i = 0; i < length; i += 1) {
28480             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28481         }
28482         
28483         if (tagType.ascii) {
28484             str = '';
28485             // Concatenate the chars:
28486             for (i = 0; i < values.length; i += 1) {
28487                 c = values[i];
28488                 // Ignore the terminating NULL byte(s):
28489                 if (c === '\u0000') {
28490                     break;
28491                 }
28492                 str += c;
28493             }
28494             return str;
28495         }
28496         return values;
28497     }
28498     
28499 });
28500
28501 Roo.apply(Roo.bootstrap.UploadCropbox, {
28502     tags : {
28503         'Orientation': 0x0112
28504     },
28505     
28506     Orientation: {
28507             1: 0, //'top-left',
28508 //            2: 'top-right',
28509             3: 180, //'bottom-right',
28510 //            4: 'bottom-left',
28511 //            5: 'left-top',
28512             6: 90, //'right-top',
28513 //            7: 'right-bottom',
28514             8: 270 //'left-bottom'
28515     },
28516     
28517     exifTagTypes : {
28518         // byte, 8-bit unsigned int:
28519         1: {
28520             getValue: function (dataView, dataOffset) {
28521                 return dataView.getUint8(dataOffset);
28522             },
28523             size: 1
28524         },
28525         // ascii, 8-bit byte:
28526         2: {
28527             getValue: function (dataView, dataOffset) {
28528                 return String.fromCharCode(dataView.getUint8(dataOffset));
28529             },
28530             size: 1,
28531             ascii: true
28532         },
28533         // short, 16 bit int:
28534         3: {
28535             getValue: function (dataView, dataOffset, littleEndian) {
28536                 return dataView.getUint16(dataOffset, littleEndian);
28537             },
28538             size: 2
28539         },
28540         // long, 32 bit int:
28541         4: {
28542             getValue: function (dataView, dataOffset, littleEndian) {
28543                 return dataView.getUint32(dataOffset, littleEndian);
28544             },
28545             size: 4
28546         },
28547         // rational = two long values, first is numerator, second is denominator:
28548         5: {
28549             getValue: function (dataView, dataOffset, littleEndian) {
28550                 return dataView.getUint32(dataOffset, littleEndian) /
28551                     dataView.getUint32(dataOffset + 4, littleEndian);
28552             },
28553             size: 8
28554         },
28555         // slong, 32 bit signed int:
28556         9: {
28557             getValue: function (dataView, dataOffset, littleEndian) {
28558                 return dataView.getInt32(dataOffset, littleEndian);
28559             },
28560             size: 4
28561         },
28562         // srational, two slongs, first is numerator, second is denominator:
28563         10: {
28564             getValue: function (dataView, dataOffset, littleEndian) {
28565                 return dataView.getInt32(dataOffset, littleEndian) /
28566                     dataView.getInt32(dataOffset + 4, littleEndian);
28567             },
28568             size: 8
28569         }
28570     },
28571     
28572     footer : {
28573         STANDARD : [
28574             {
28575                 tag : 'div',
28576                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28577                 action : 'rotate-left',
28578                 cn : [
28579                     {
28580                         tag : 'button',
28581                         cls : 'btn btn-default',
28582                         html : '<i class="fa fa-undo"></i>'
28583                     }
28584                 ]
28585             },
28586             {
28587                 tag : 'div',
28588                 cls : 'btn-group roo-upload-cropbox-picture',
28589                 action : 'picture',
28590                 cn : [
28591                     {
28592                         tag : 'button',
28593                         cls : 'btn btn-default',
28594                         html : '<i class="fa fa-picture-o"></i>'
28595                     }
28596                 ]
28597             },
28598             {
28599                 tag : 'div',
28600                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28601                 action : 'rotate-right',
28602                 cn : [
28603                     {
28604                         tag : 'button',
28605                         cls : 'btn btn-default',
28606                         html : '<i class="fa fa-repeat"></i>'
28607                     }
28608                 ]
28609             }
28610         ],
28611         DOCUMENT : [
28612             {
28613                 tag : 'div',
28614                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28615                 action : 'rotate-left',
28616                 cn : [
28617                     {
28618                         tag : 'button',
28619                         cls : 'btn btn-default',
28620                         html : '<i class="fa fa-undo"></i>'
28621                     }
28622                 ]
28623             },
28624             {
28625                 tag : 'div',
28626                 cls : 'btn-group roo-upload-cropbox-download',
28627                 action : 'download',
28628                 cn : [
28629                     {
28630                         tag : 'button',
28631                         cls : 'btn btn-default',
28632                         html : '<i class="fa fa-download"></i>'
28633                     }
28634                 ]
28635             },
28636             {
28637                 tag : 'div',
28638                 cls : 'btn-group roo-upload-cropbox-crop',
28639                 action : 'crop',
28640                 cn : [
28641                     {
28642                         tag : 'button',
28643                         cls : 'btn btn-default',
28644                         html : '<i class="fa fa-crop"></i>'
28645                     }
28646                 ]
28647             },
28648             {
28649                 tag : 'div',
28650                 cls : 'btn-group roo-upload-cropbox-trash',
28651                 action : 'trash',
28652                 cn : [
28653                     {
28654                         tag : 'button',
28655                         cls : 'btn btn-default',
28656                         html : '<i class="fa fa-trash"></i>'
28657                     }
28658                 ]
28659             },
28660             {
28661                 tag : 'div',
28662                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28663                 action : 'rotate-right',
28664                 cn : [
28665                     {
28666                         tag : 'button',
28667                         cls : 'btn btn-default',
28668                         html : '<i class="fa fa-repeat"></i>'
28669                     }
28670                 ]
28671             }
28672         ],
28673         ROTATOR : [
28674             {
28675                 tag : 'div',
28676                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28677                 action : 'rotate-left',
28678                 cn : [
28679                     {
28680                         tag : 'button',
28681                         cls : 'btn btn-default',
28682                         html : '<i class="fa fa-undo"></i>'
28683                     }
28684                 ]
28685             },
28686             {
28687                 tag : 'div',
28688                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28689                 action : 'rotate-right',
28690                 cn : [
28691                     {
28692                         tag : 'button',
28693                         cls : 'btn btn-default',
28694                         html : '<i class="fa fa-repeat"></i>'
28695                     }
28696                 ]
28697             }
28698         ]
28699     }
28700 });
28701
28702 /*
28703 * Licence: LGPL
28704 */
28705
28706 /**
28707  * @class Roo.bootstrap.DocumentManager
28708  * @extends Roo.bootstrap.Component
28709  * Bootstrap DocumentManager class
28710  * @cfg {String} paramName default 'imageUpload'
28711  * @cfg {String} toolTipName default 'filename'
28712  * @cfg {String} method default POST
28713  * @cfg {String} url action url
28714  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28715  * @cfg {Boolean} multiple multiple upload default true
28716  * @cfg {Number} thumbSize default 300
28717  * @cfg {String} fieldLabel
28718  * @cfg {Number} labelWidth default 4
28719  * @cfg {String} labelAlign (left|top) default left
28720  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28721 * @cfg {Number} labellg set the width of label (1-12)
28722  * @cfg {Number} labelmd set the width of label (1-12)
28723  * @cfg {Number} labelsm set the width of label (1-12)
28724  * @cfg {Number} labelxs set the width of label (1-12)
28725  * 
28726  * @constructor
28727  * Create a new DocumentManager
28728  * @param {Object} config The config object
28729  */
28730
28731 Roo.bootstrap.DocumentManager = function(config){
28732     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28733     
28734     this.files = [];
28735     this.delegates = [];
28736     
28737     this.addEvents({
28738         /**
28739          * @event initial
28740          * Fire when initial the DocumentManager
28741          * @param {Roo.bootstrap.DocumentManager} this
28742          */
28743         "initial" : true,
28744         /**
28745          * @event inspect
28746          * inspect selected file
28747          * @param {Roo.bootstrap.DocumentManager} this
28748          * @param {File} file
28749          */
28750         "inspect" : true,
28751         /**
28752          * @event exception
28753          * Fire when xhr load exception
28754          * @param {Roo.bootstrap.DocumentManager} this
28755          * @param {XMLHttpRequest} xhr
28756          */
28757         "exception" : true,
28758         /**
28759          * @event afterupload
28760          * Fire when xhr load exception
28761          * @param {Roo.bootstrap.DocumentManager} this
28762          * @param {XMLHttpRequest} xhr
28763          */
28764         "afterupload" : true,
28765         /**
28766          * @event prepare
28767          * prepare the form data
28768          * @param {Roo.bootstrap.DocumentManager} this
28769          * @param {Object} formData
28770          */
28771         "prepare" : true,
28772         /**
28773          * @event remove
28774          * Fire when remove the file
28775          * @param {Roo.bootstrap.DocumentManager} this
28776          * @param {Object} file
28777          */
28778         "remove" : true,
28779         /**
28780          * @event refresh
28781          * Fire after refresh the file
28782          * @param {Roo.bootstrap.DocumentManager} this
28783          */
28784         "refresh" : true,
28785         /**
28786          * @event click
28787          * Fire after click the image
28788          * @param {Roo.bootstrap.DocumentManager} this
28789          * @param {Object} file
28790          */
28791         "click" : true,
28792         /**
28793          * @event edit
28794          * Fire when upload a image and editable set to true
28795          * @param {Roo.bootstrap.DocumentManager} this
28796          * @param {Object} file
28797          */
28798         "edit" : true,
28799         /**
28800          * @event beforeselectfile
28801          * Fire before select file
28802          * @param {Roo.bootstrap.DocumentManager} this
28803          */
28804         "beforeselectfile" : true,
28805         /**
28806          * @event process
28807          * Fire before process file
28808          * @param {Roo.bootstrap.DocumentManager} this
28809          * @param {Object} file
28810          */
28811         "process" : true,
28812         /**
28813          * @event previewrendered
28814          * Fire when preview rendered
28815          * @param {Roo.bootstrap.DocumentManager} this
28816          * @param {Object} file
28817          */
28818         "previewrendered" : true,
28819         /**
28820          */
28821         "previewResize" : true
28822         
28823     });
28824 };
28825
28826 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28827     
28828     boxes : 0,
28829     inputName : '',
28830     thumbSize : 300,
28831     multiple : true,
28832     files : false,
28833     method : 'POST',
28834     url : '',
28835     paramName : 'imageUpload',
28836     toolTipName : 'filename',
28837     fieldLabel : '',
28838     labelWidth : 4,
28839     labelAlign : 'left',
28840     editable : true,
28841     delegates : false,
28842     xhr : false, 
28843     
28844     labellg : 0,
28845     labelmd : 0,
28846     labelsm : 0,
28847     labelxs : 0,
28848     
28849     getAutoCreate : function()
28850     {   
28851         var managerWidget = {
28852             tag : 'div',
28853             cls : 'roo-document-manager',
28854             cn : [
28855                 {
28856                     tag : 'input',
28857                     cls : 'roo-document-manager-selector',
28858                     type : 'file'
28859                 },
28860                 {
28861                     tag : 'div',
28862                     cls : 'roo-document-manager-uploader',
28863                     cn : [
28864                         {
28865                             tag : 'div',
28866                             cls : 'roo-document-manager-upload-btn',
28867                             html : '<i class="fa fa-plus"></i>'
28868                         }
28869                     ]
28870                     
28871                 }
28872             ]
28873         };
28874         
28875         var content = [
28876             {
28877                 tag : 'div',
28878                 cls : 'column col-md-12',
28879                 cn : managerWidget
28880             }
28881         ];
28882         
28883         if(this.fieldLabel.length){
28884             
28885             content = [
28886                 {
28887                     tag : 'div',
28888                     cls : 'column col-md-12',
28889                     html : this.fieldLabel
28890                 },
28891                 {
28892                     tag : 'div',
28893                     cls : 'column col-md-12',
28894                     cn : managerWidget
28895                 }
28896             ];
28897
28898             if(this.labelAlign == 'left'){
28899                 content = [
28900                     {
28901                         tag : 'div',
28902                         cls : 'column',
28903                         html : this.fieldLabel
28904                     },
28905                     {
28906                         tag : 'div',
28907                         cls : 'column',
28908                         cn : managerWidget
28909                     }
28910                 ];
28911                 
28912                 if(this.labelWidth > 12){
28913                     content[0].style = "width: " + this.labelWidth + 'px';
28914                 }
28915
28916                 if(this.labelWidth < 13 && this.labelmd == 0){
28917                     this.labelmd = this.labelWidth;
28918                 }
28919
28920                 if(this.labellg > 0){
28921                     content[0].cls += ' col-lg-' + this.labellg;
28922                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28923                 }
28924
28925                 if(this.labelmd > 0){
28926                     content[0].cls += ' col-md-' + this.labelmd;
28927                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28928                 }
28929
28930                 if(this.labelsm > 0){
28931                     content[0].cls += ' col-sm-' + this.labelsm;
28932                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28933                 }
28934
28935                 if(this.labelxs > 0){
28936                     content[0].cls += ' col-xs-' + this.labelxs;
28937                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28938                 }
28939                 
28940             }
28941         }
28942         
28943         var cfg = {
28944             tag : 'div',
28945             cls : 'row clearfix',
28946             cn : content
28947         };
28948         
28949         return cfg;
28950         
28951     },
28952     
28953     initEvents : function()
28954     {
28955         this.managerEl = this.el.select('.roo-document-manager', true).first();
28956         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28957         
28958         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28959         this.selectorEl.hide();
28960         
28961         if(this.multiple){
28962             this.selectorEl.attr('multiple', 'multiple');
28963         }
28964         
28965         this.selectorEl.on('change', this.onFileSelected, this);
28966         
28967         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28968         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28969         
28970         this.uploader.on('click', this.onUploaderClick, this);
28971         
28972         this.renderProgressDialog();
28973         
28974         var _this = this;
28975         
28976         window.addEventListener("resize", function() { _this.refresh(); } );
28977         
28978         this.fireEvent('initial', this);
28979     },
28980     
28981     renderProgressDialog : function()
28982     {
28983         var _this = this;
28984         
28985         this.progressDialog = new Roo.bootstrap.Modal({
28986             cls : 'roo-document-manager-progress-dialog',
28987             allow_close : false,
28988             title : '',
28989             buttons : [
28990                 {
28991                     name  :'cancel',
28992                     weight : 'danger',
28993                     html : 'Cancel'
28994                 }
28995             ], 
28996             listeners : { 
28997                 btnclick : function() {
28998                     _this.uploadCancel();
28999                     this.hide();
29000                 }
29001             }
29002         });
29003          
29004         this.progressDialog.render(Roo.get(document.body));
29005          
29006         this.progress = new Roo.bootstrap.Progress({
29007             cls : 'roo-document-manager-progress',
29008             active : true,
29009             striped : true
29010         });
29011         
29012         this.progress.render(this.progressDialog.getChildContainer());
29013         
29014         this.progressBar = new Roo.bootstrap.ProgressBar({
29015             cls : 'roo-document-manager-progress-bar',
29016             aria_valuenow : 0,
29017             aria_valuemin : 0,
29018             aria_valuemax : 12,
29019             panel : 'success'
29020         });
29021         
29022         this.progressBar.render(this.progress.getChildContainer());
29023     },
29024     
29025     onUploaderClick : function(e)
29026     {
29027         e.preventDefault();
29028      
29029         if(this.fireEvent('beforeselectfile', this) != false){
29030             this.selectorEl.dom.click();
29031         }
29032         
29033     },
29034     
29035     onFileSelected : function(e)
29036     {
29037         e.preventDefault();
29038         
29039         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29040             return;
29041         }
29042         
29043         Roo.each(this.selectorEl.dom.files, function(file){
29044             if(this.fireEvent('inspect', this, file) != false){
29045                 this.files.push(file);
29046             }
29047         }, this);
29048         
29049         this.queue();
29050         
29051     },
29052     
29053     queue : function()
29054     {
29055         this.selectorEl.dom.value = '';
29056         
29057         if(!this.files || !this.files.length){
29058             return;
29059         }
29060         
29061         if(this.boxes > 0 && this.files.length > this.boxes){
29062             this.files = this.files.slice(0, this.boxes);
29063         }
29064         
29065         this.uploader.show();
29066         
29067         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29068             this.uploader.hide();
29069         }
29070         
29071         var _this = this;
29072         
29073         var files = [];
29074         
29075         var docs = [];
29076         
29077         Roo.each(this.files, function(file){
29078             
29079             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29080                 var f = this.renderPreview(file);
29081                 files.push(f);
29082                 return;
29083             }
29084             
29085             if(file.type.indexOf('image') != -1){
29086                 this.delegates.push(
29087                     (function(){
29088                         _this.process(file);
29089                     }).createDelegate(this)
29090                 );
29091         
29092                 return;
29093             }
29094             
29095             docs.push(
29096                 (function(){
29097                     _this.process(file);
29098                 }).createDelegate(this)
29099             );
29100             
29101         }, this);
29102         
29103         this.files = files;
29104         
29105         this.delegates = this.delegates.concat(docs);
29106         
29107         if(!this.delegates.length){
29108             this.refresh();
29109             return;
29110         }
29111         
29112         this.progressBar.aria_valuemax = this.delegates.length;
29113         
29114         this.arrange();
29115         
29116         return;
29117     },
29118     
29119     arrange : function()
29120     {
29121         if(!this.delegates.length){
29122             this.progressDialog.hide();
29123             this.refresh();
29124             return;
29125         }
29126         
29127         var delegate = this.delegates.shift();
29128         
29129         this.progressDialog.show();
29130         
29131         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29132         
29133         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29134         
29135         delegate();
29136     },
29137     
29138     refresh : function()
29139     {
29140         this.uploader.show();
29141         
29142         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29143             this.uploader.hide();
29144         }
29145         
29146         Roo.isTouch ? this.closable(false) : this.closable(true);
29147         
29148         this.fireEvent('refresh', this);
29149     },
29150     
29151     onRemove : function(e, el, o)
29152     {
29153         e.preventDefault();
29154         
29155         this.fireEvent('remove', this, o);
29156         
29157     },
29158     
29159     remove : function(o)
29160     {
29161         var files = [];
29162         
29163         Roo.each(this.files, function(file){
29164             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29165                 files.push(file);
29166                 return;
29167             }
29168
29169             o.target.remove();
29170
29171         }, this);
29172         
29173         this.files = files;
29174         
29175         this.refresh();
29176     },
29177     
29178     clear : function()
29179     {
29180         Roo.each(this.files, function(file){
29181             if(!file.target){
29182                 return;
29183             }
29184             
29185             file.target.remove();
29186
29187         }, this);
29188         
29189         this.files = [];
29190         
29191         this.refresh();
29192     },
29193     
29194     onClick : function(e, el, o)
29195     {
29196         e.preventDefault();
29197         
29198         this.fireEvent('click', this, o);
29199         
29200     },
29201     
29202     closable : function(closable)
29203     {
29204         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29205             
29206             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29207             
29208             if(closable){
29209                 el.show();
29210                 return;
29211             }
29212             
29213             el.hide();
29214             
29215         }, this);
29216     },
29217     
29218     xhrOnLoad : function(xhr)
29219     {
29220         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29221             el.remove();
29222         }, this);
29223         
29224         if (xhr.readyState !== 4) {
29225             this.arrange();
29226             this.fireEvent('exception', this, xhr);
29227             return;
29228         }
29229
29230         var response = Roo.decode(xhr.responseText);
29231         
29232         if(!response.success){
29233             this.arrange();
29234             this.fireEvent('exception', this, xhr);
29235             return;
29236         }
29237         
29238         var file = this.renderPreview(response.data);
29239         
29240         this.files.push(file);
29241         
29242         this.arrange();
29243         
29244         this.fireEvent('afterupload', this, xhr);
29245         
29246     },
29247     
29248     xhrOnError : function(xhr)
29249     {
29250         Roo.log('xhr on error');
29251         
29252         var response = Roo.decode(xhr.responseText);
29253           
29254         Roo.log(response);
29255         
29256         this.arrange();
29257     },
29258     
29259     process : function(file)
29260     {
29261         if(this.fireEvent('process', this, file) !== false){
29262             if(this.editable && file.type.indexOf('image') != -1){
29263                 this.fireEvent('edit', this, file);
29264                 return;
29265             }
29266
29267             this.uploadStart(file, false);
29268
29269             return;
29270         }
29271         
29272     },
29273     
29274     uploadStart : function(file, crop)
29275     {
29276         this.xhr = new XMLHttpRequest();
29277         
29278         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29279             this.arrange();
29280             return;
29281         }
29282         
29283         file.xhr = this.xhr;
29284             
29285         this.managerEl.createChild({
29286             tag : 'div',
29287             cls : 'roo-document-manager-loading',
29288             cn : [
29289                 {
29290                     tag : 'div',
29291                     tooltip : file.name,
29292                     cls : 'roo-document-manager-thumb',
29293                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29294                 }
29295             ]
29296
29297         });
29298
29299         this.xhr.open(this.method, this.url, true);
29300         
29301         var headers = {
29302             "Accept": "application/json",
29303             "Cache-Control": "no-cache",
29304             "X-Requested-With": "XMLHttpRequest"
29305         };
29306         
29307         for (var headerName in headers) {
29308             var headerValue = headers[headerName];
29309             if (headerValue) {
29310                 this.xhr.setRequestHeader(headerName, headerValue);
29311             }
29312         }
29313         
29314         var _this = this;
29315         
29316         this.xhr.onload = function()
29317         {
29318             _this.xhrOnLoad(_this.xhr);
29319         }
29320         
29321         this.xhr.onerror = function()
29322         {
29323             _this.xhrOnError(_this.xhr);
29324         }
29325         
29326         var formData = new FormData();
29327
29328         formData.append('returnHTML', 'NO');
29329         
29330         if(crop){
29331             formData.append('crop', crop);
29332         }
29333         
29334         formData.append(this.paramName, file, file.name);
29335         
29336         var options = {
29337             file : file, 
29338             manually : false
29339         };
29340         
29341         if(this.fireEvent('prepare', this, formData, options) != false){
29342             
29343             if(options.manually){
29344                 return;
29345             }
29346             
29347             this.xhr.send(formData);
29348             return;
29349         };
29350         
29351         this.uploadCancel();
29352     },
29353     
29354     uploadCancel : function()
29355     {
29356         if (this.xhr) {
29357             this.xhr.abort();
29358         }
29359         
29360         this.delegates = [];
29361         
29362         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29363             el.remove();
29364         }, this);
29365         
29366         this.arrange();
29367     },
29368     
29369     renderPreview : function(file)
29370     {
29371         if(typeof(file.target) != 'undefined' && file.target){
29372             return file;
29373         }
29374         
29375         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29376         
29377         var previewEl = this.managerEl.createChild({
29378             tag : 'div',
29379             cls : 'roo-document-manager-preview',
29380             cn : [
29381                 {
29382                     tag : 'div',
29383                     tooltip : file[this.toolTipName],
29384                     cls : 'roo-document-manager-thumb',
29385                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29386                 },
29387                 {
29388                     tag : 'button',
29389                     cls : 'close',
29390                     html : '<i class="fa fa-times-circle"></i>'
29391                 }
29392             ]
29393         });
29394
29395         var close = previewEl.select('button.close', true).first();
29396
29397         close.on('click', this.onRemove, this, file);
29398
29399         file.target = previewEl;
29400
29401         var image = previewEl.select('img', true).first();
29402         
29403         var _this = this;
29404         
29405         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29406         
29407         image.on('click', this.onClick, this, file);
29408         
29409         this.fireEvent('previewrendered', this, file);
29410         
29411         return file;
29412         
29413     },
29414     
29415     onPreviewLoad : function(file, image)
29416     {
29417         if(typeof(file.target) == 'undefined' || !file.target){
29418             return;
29419         }
29420         
29421         var width = image.dom.naturalWidth || image.dom.width;
29422         var height = image.dom.naturalHeight || image.dom.height;
29423         
29424         if(!this.previewResize) {
29425             return;
29426         }
29427         
29428         if(width > height){
29429             file.target.addClass('wide');
29430             return;
29431         }
29432         
29433         file.target.addClass('tall');
29434         return;
29435         
29436     },
29437     
29438     uploadFromSource : function(file, crop)
29439     {
29440         this.xhr = new XMLHttpRequest();
29441         
29442         this.managerEl.createChild({
29443             tag : 'div',
29444             cls : 'roo-document-manager-loading',
29445             cn : [
29446                 {
29447                     tag : 'div',
29448                     tooltip : file.name,
29449                     cls : 'roo-document-manager-thumb',
29450                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29451                 }
29452             ]
29453
29454         });
29455
29456         this.xhr.open(this.method, this.url, true);
29457         
29458         var headers = {
29459             "Accept": "application/json",
29460             "Cache-Control": "no-cache",
29461             "X-Requested-With": "XMLHttpRequest"
29462         };
29463         
29464         for (var headerName in headers) {
29465             var headerValue = headers[headerName];
29466             if (headerValue) {
29467                 this.xhr.setRequestHeader(headerName, headerValue);
29468             }
29469         }
29470         
29471         var _this = this;
29472         
29473         this.xhr.onload = function()
29474         {
29475             _this.xhrOnLoad(_this.xhr);
29476         }
29477         
29478         this.xhr.onerror = function()
29479         {
29480             _this.xhrOnError(_this.xhr);
29481         }
29482         
29483         var formData = new FormData();
29484
29485         formData.append('returnHTML', 'NO');
29486         
29487         formData.append('crop', crop);
29488         
29489         if(typeof(file.filename) != 'undefined'){
29490             formData.append('filename', file.filename);
29491         }
29492         
29493         if(typeof(file.mimetype) != 'undefined'){
29494             formData.append('mimetype', file.mimetype);
29495         }
29496         
29497         Roo.log(formData);
29498         
29499         if(this.fireEvent('prepare', this, formData) != false){
29500             this.xhr.send(formData);
29501         };
29502     }
29503 });
29504
29505 /*
29506 * Licence: LGPL
29507 */
29508
29509 /**
29510  * @class Roo.bootstrap.DocumentViewer
29511  * @extends Roo.bootstrap.Component
29512  * Bootstrap DocumentViewer class
29513  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29514  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29515  * 
29516  * @constructor
29517  * Create a new DocumentViewer
29518  * @param {Object} config The config object
29519  */
29520
29521 Roo.bootstrap.DocumentViewer = function(config){
29522     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29523     
29524     this.addEvents({
29525         /**
29526          * @event initial
29527          * Fire after initEvent
29528          * @param {Roo.bootstrap.DocumentViewer} this
29529          */
29530         "initial" : true,
29531         /**
29532          * @event click
29533          * Fire after click
29534          * @param {Roo.bootstrap.DocumentViewer} this
29535          */
29536         "click" : true,
29537         /**
29538          * @event download
29539          * Fire after download button
29540          * @param {Roo.bootstrap.DocumentViewer} this
29541          */
29542         "download" : true,
29543         /**
29544          * @event trash
29545          * Fire after trash button
29546          * @param {Roo.bootstrap.DocumentViewer} this
29547          */
29548         "trash" : true
29549         
29550     });
29551 };
29552
29553 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29554     
29555     showDownload : true,
29556     
29557     showTrash : true,
29558     
29559     getAutoCreate : function()
29560     {
29561         var cfg = {
29562             tag : 'div',
29563             cls : 'roo-document-viewer',
29564             cn : [
29565                 {
29566                     tag : 'div',
29567                     cls : 'roo-document-viewer-body',
29568                     cn : [
29569                         {
29570                             tag : 'div',
29571                             cls : 'roo-document-viewer-thumb',
29572                             cn : [
29573                                 {
29574                                     tag : 'img',
29575                                     cls : 'roo-document-viewer-image'
29576                                 }
29577                             ]
29578                         }
29579                     ]
29580                 },
29581                 {
29582                     tag : 'div',
29583                     cls : 'roo-document-viewer-footer',
29584                     cn : {
29585                         tag : 'div',
29586                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29587                         cn : [
29588                             {
29589                                 tag : 'div',
29590                                 cls : 'btn-group roo-document-viewer-download',
29591                                 cn : [
29592                                     {
29593                                         tag : 'button',
29594                                         cls : 'btn btn-default',
29595                                         html : '<i class="fa fa-download"></i>'
29596                                     }
29597                                 ]
29598                             },
29599                             {
29600                                 tag : 'div',
29601                                 cls : 'btn-group roo-document-viewer-trash',
29602                                 cn : [
29603                                     {
29604                                         tag : 'button',
29605                                         cls : 'btn btn-default',
29606                                         html : '<i class="fa fa-trash"></i>'
29607                                     }
29608                                 ]
29609                             }
29610                         ]
29611                     }
29612                 }
29613             ]
29614         };
29615         
29616         return cfg;
29617     },
29618     
29619     initEvents : function()
29620     {
29621         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29622         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29623         
29624         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29625         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29626         
29627         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29628         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29629         
29630         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29631         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29632         
29633         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29634         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29635         
29636         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29637         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29638         
29639         this.bodyEl.on('click', this.onClick, this);
29640         this.downloadBtn.on('click', this.onDownload, this);
29641         this.trashBtn.on('click', this.onTrash, this);
29642         
29643         this.downloadBtn.hide();
29644         this.trashBtn.hide();
29645         
29646         if(this.showDownload){
29647             this.downloadBtn.show();
29648         }
29649         
29650         if(this.showTrash){
29651             this.trashBtn.show();
29652         }
29653         
29654         if(!this.showDownload && !this.showTrash) {
29655             this.footerEl.hide();
29656         }
29657         
29658     },
29659     
29660     initial : function()
29661     {
29662         this.fireEvent('initial', this);
29663         
29664     },
29665     
29666     onClick : function(e)
29667     {
29668         e.preventDefault();
29669         
29670         this.fireEvent('click', this);
29671     },
29672     
29673     onDownload : function(e)
29674     {
29675         e.preventDefault();
29676         
29677         this.fireEvent('download', this);
29678     },
29679     
29680     onTrash : function(e)
29681     {
29682         e.preventDefault();
29683         
29684         this.fireEvent('trash', this);
29685     }
29686     
29687 });
29688 /*
29689  * - LGPL
29690  *
29691  * nav progress bar
29692  * 
29693  */
29694
29695 /**
29696  * @class Roo.bootstrap.NavProgressBar
29697  * @extends Roo.bootstrap.Component
29698  * Bootstrap NavProgressBar class
29699  * 
29700  * @constructor
29701  * Create a new nav progress bar
29702  * @param {Object} config The config object
29703  */
29704
29705 Roo.bootstrap.NavProgressBar = function(config){
29706     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29707
29708     this.bullets = this.bullets || [];
29709    
29710 //    Roo.bootstrap.NavProgressBar.register(this);
29711      this.addEvents({
29712         /**
29713              * @event changed
29714              * Fires when the active item changes
29715              * @param {Roo.bootstrap.NavProgressBar} this
29716              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29717              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29718          */
29719         'changed': true
29720      });
29721     
29722 };
29723
29724 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29725     
29726     bullets : [],
29727     barItems : [],
29728     
29729     getAutoCreate : function()
29730     {
29731         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29732         
29733         cfg = {
29734             tag : 'div',
29735             cls : 'roo-navigation-bar-group',
29736             cn : [
29737                 {
29738                     tag : 'div',
29739                     cls : 'roo-navigation-top-bar'
29740                 },
29741                 {
29742                     tag : 'div',
29743                     cls : 'roo-navigation-bullets-bar',
29744                     cn : [
29745                         {
29746                             tag : 'ul',
29747                             cls : 'roo-navigation-bar'
29748                         }
29749                     ]
29750                 },
29751                 
29752                 {
29753                     tag : 'div',
29754                     cls : 'roo-navigation-bottom-bar'
29755                 }
29756             ]
29757             
29758         };
29759         
29760         return cfg;
29761         
29762     },
29763     
29764     initEvents: function() 
29765     {
29766         
29767     },
29768     
29769     onRender : function(ct, position) 
29770     {
29771         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29772         
29773         if(this.bullets.length){
29774             Roo.each(this.bullets, function(b){
29775                this.addItem(b);
29776             }, this);
29777         }
29778         
29779         this.format();
29780         
29781     },
29782     
29783     addItem : function(cfg)
29784     {
29785         var item = new Roo.bootstrap.NavProgressItem(cfg);
29786         
29787         item.parentId = this.id;
29788         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29789         
29790         if(cfg.html){
29791             var top = new Roo.bootstrap.Element({
29792                 tag : 'div',
29793                 cls : 'roo-navigation-bar-text'
29794             });
29795             
29796             var bottom = new Roo.bootstrap.Element({
29797                 tag : 'div',
29798                 cls : 'roo-navigation-bar-text'
29799             });
29800             
29801             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29802             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29803             
29804             var topText = new Roo.bootstrap.Element({
29805                 tag : 'span',
29806                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29807             });
29808             
29809             var bottomText = new Roo.bootstrap.Element({
29810                 tag : 'span',
29811                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29812             });
29813             
29814             topText.onRender(top.el, null);
29815             bottomText.onRender(bottom.el, null);
29816             
29817             item.topEl = top;
29818             item.bottomEl = bottom;
29819         }
29820         
29821         this.barItems.push(item);
29822         
29823         return item;
29824     },
29825     
29826     getActive : function()
29827     {
29828         var active = false;
29829         
29830         Roo.each(this.barItems, function(v){
29831             
29832             if (!v.isActive()) {
29833                 return;
29834             }
29835             
29836             active = v;
29837             return false;
29838             
29839         });
29840         
29841         return active;
29842     },
29843     
29844     setActiveItem : function(item)
29845     {
29846         var prev = false;
29847         
29848         Roo.each(this.barItems, function(v){
29849             if (v.rid == item.rid) {
29850                 return ;
29851             }
29852             
29853             if (v.isActive()) {
29854                 v.setActive(false);
29855                 prev = v;
29856             }
29857         });
29858
29859         item.setActive(true);
29860         
29861         this.fireEvent('changed', this, item, prev);
29862     },
29863     
29864     getBarItem: function(rid)
29865     {
29866         var ret = false;
29867         
29868         Roo.each(this.barItems, function(e) {
29869             if (e.rid != rid) {
29870                 return;
29871             }
29872             
29873             ret =  e;
29874             return false;
29875         });
29876         
29877         return ret;
29878     },
29879     
29880     indexOfItem : function(item)
29881     {
29882         var index = false;
29883         
29884         Roo.each(this.barItems, function(v, i){
29885             
29886             if (v.rid != item.rid) {
29887                 return;
29888             }
29889             
29890             index = i;
29891             return false
29892         });
29893         
29894         return index;
29895     },
29896     
29897     setActiveNext : function()
29898     {
29899         var i = this.indexOfItem(this.getActive());
29900         
29901         if (i > this.barItems.length) {
29902             return;
29903         }
29904         
29905         this.setActiveItem(this.barItems[i+1]);
29906     },
29907     
29908     setActivePrev : function()
29909     {
29910         var i = this.indexOfItem(this.getActive());
29911         
29912         if (i  < 1) {
29913             return;
29914         }
29915         
29916         this.setActiveItem(this.barItems[i-1]);
29917     },
29918     
29919     format : function()
29920     {
29921         if(!this.barItems.length){
29922             return;
29923         }
29924      
29925         var width = 100 / this.barItems.length;
29926         
29927         Roo.each(this.barItems, function(i){
29928             i.el.setStyle('width', width + '%');
29929             i.topEl.el.setStyle('width', width + '%');
29930             i.bottomEl.el.setStyle('width', width + '%');
29931         }, this);
29932         
29933     }
29934     
29935 });
29936 /*
29937  * - LGPL
29938  *
29939  * Nav Progress Item
29940  * 
29941  */
29942
29943 /**
29944  * @class Roo.bootstrap.NavProgressItem
29945  * @extends Roo.bootstrap.Component
29946  * Bootstrap NavProgressItem class
29947  * @cfg {String} rid the reference id
29948  * @cfg {Boolean} active (true|false) Is item active default false
29949  * @cfg {Boolean} disabled (true|false) Is item active default false
29950  * @cfg {String} html
29951  * @cfg {String} position (top|bottom) text position default bottom
29952  * @cfg {String} icon show icon instead of number
29953  * 
29954  * @constructor
29955  * Create a new NavProgressItem
29956  * @param {Object} config The config object
29957  */
29958 Roo.bootstrap.NavProgressItem = function(config){
29959     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29960     this.addEvents({
29961         // raw events
29962         /**
29963          * @event click
29964          * The raw click event for the entire grid.
29965          * @param {Roo.bootstrap.NavProgressItem} this
29966          * @param {Roo.EventObject} e
29967          */
29968         "click" : true
29969     });
29970    
29971 };
29972
29973 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29974     
29975     rid : '',
29976     active : false,
29977     disabled : false,
29978     html : '',
29979     position : 'bottom',
29980     icon : false,
29981     
29982     getAutoCreate : function()
29983     {
29984         var iconCls = 'roo-navigation-bar-item-icon';
29985         
29986         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29987         
29988         var cfg = {
29989             tag: 'li',
29990             cls: 'roo-navigation-bar-item',
29991             cn : [
29992                 {
29993                     tag : 'i',
29994                     cls : iconCls
29995                 }
29996             ]
29997         };
29998         
29999         if(this.active){
30000             cfg.cls += ' active';
30001         }
30002         if(this.disabled){
30003             cfg.cls += ' disabled';
30004         }
30005         
30006         return cfg;
30007     },
30008     
30009     disable : function()
30010     {
30011         this.setDisabled(true);
30012     },
30013     
30014     enable : function()
30015     {
30016         this.setDisabled(false);
30017     },
30018     
30019     initEvents: function() 
30020     {
30021         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30022         
30023         this.iconEl.on('click', this.onClick, this);
30024     },
30025     
30026     onClick : function(e)
30027     {
30028         e.preventDefault();
30029         
30030         if(this.disabled){
30031             return;
30032         }
30033         
30034         if(this.fireEvent('click', this, e) === false){
30035             return;
30036         };
30037         
30038         this.parent().setActiveItem(this);
30039     },
30040     
30041     isActive: function () 
30042     {
30043         return this.active;
30044     },
30045     
30046     setActive : function(state)
30047     {
30048         if(this.active == state){
30049             return;
30050         }
30051         
30052         this.active = state;
30053         
30054         if (state) {
30055             this.el.addClass('active');
30056             return;
30057         }
30058         
30059         this.el.removeClass('active');
30060         
30061         return;
30062     },
30063     
30064     setDisabled : function(state)
30065     {
30066         if(this.disabled == state){
30067             return;
30068         }
30069         
30070         this.disabled = state;
30071         
30072         if (state) {
30073             this.el.addClass('disabled');
30074             return;
30075         }
30076         
30077         this.el.removeClass('disabled');
30078     },
30079     
30080     tooltipEl : function()
30081     {
30082         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30083     }
30084 });
30085  
30086
30087  /*
30088  * - LGPL
30089  *
30090  * FieldLabel
30091  * 
30092  */
30093
30094 /**
30095  * @class Roo.bootstrap.FieldLabel
30096  * @extends Roo.bootstrap.Component
30097  * Bootstrap FieldLabel class
30098  * @cfg {String} html contents of the element
30099  * @cfg {String} tag tag of the element default label
30100  * @cfg {String} cls class of the element
30101  * @cfg {String} target label target 
30102  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30103  * @cfg {String} invalidClass default "text-warning"
30104  * @cfg {String} validClass default "text-success"
30105  * @cfg {String} iconTooltip default "This field is required"
30106  * @cfg {String} indicatorpos (left|right) default left
30107  * 
30108  * @constructor
30109  * Create a new FieldLabel
30110  * @param {Object} config The config object
30111  */
30112
30113 Roo.bootstrap.FieldLabel = function(config){
30114     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30115     
30116     this.addEvents({
30117             /**
30118              * @event invalid
30119              * Fires after the field has been marked as invalid.
30120              * @param {Roo.form.FieldLabel} this
30121              * @param {String} msg The validation message
30122              */
30123             invalid : true,
30124             /**
30125              * @event valid
30126              * Fires after the field has been validated with no errors.
30127              * @param {Roo.form.FieldLabel} this
30128              */
30129             valid : true
30130         });
30131 };
30132
30133 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30134     
30135     tag: 'label',
30136     cls: '',
30137     html: '',
30138     target: '',
30139     allowBlank : true,
30140     invalidClass : 'has-warning',
30141     validClass : 'has-success',
30142     iconTooltip : 'This field is required',
30143     indicatorpos : 'left',
30144     
30145     getAutoCreate : function(){
30146         
30147         var cls = "";
30148         if (!this.allowBlank) {
30149             cls  = "visible";
30150         }
30151         
30152         var cfg = {
30153             tag : this.tag,
30154             cls : 'roo-bootstrap-field-label ' + this.cls,
30155             for : this.target,
30156             cn : [
30157                 {
30158                     tag : 'i',
30159                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30160                     tooltip : this.iconTooltip
30161                 },
30162                 {
30163                     tag : 'span',
30164                     html : this.html
30165                 }
30166             ] 
30167         };
30168         
30169         if(this.indicatorpos == 'right'){
30170             var cfg = {
30171                 tag : this.tag,
30172                 cls : 'roo-bootstrap-field-label ' + this.cls,
30173                 for : this.target,
30174                 cn : [
30175                     {
30176                         tag : 'span',
30177                         html : this.html
30178                     },
30179                     {
30180                         tag : 'i',
30181                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30182                         tooltip : this.iconTooltip
30183                     }
30184                 ] 
30185             };
30186         }
30187         
30188         return cfg;
30189     },
30190     
30191     initEvents: function() 
30192     {
30193         Roo.bootstrap.Element.superclass.initEvents.call(this);
30194         
30195         this.indicator = this.indicatorEl();
30196         
30197         if(this.indicator){
30198             this.indicator.removeClass('visible');
30199             this.indicator.addClass('invisible');
30200         }
30201         
30202         Roo.bootstrap.FieldLabel.register(this);
30203     },
30204     
30205     indicatorEl : function()
30206     {
30207         var indicator = this.el.select('i.roo-required-indicator',true).first();
30208         
30209         if(!indicator){
30210             return false;
30211         }
30212         
30213         return indicator;
30214         
30215     },
30216     
30217     /**
30218      * Mark this field as valid
30219      */
30220     markValid : function()
30221     {
30222         if(this.indicator){
30223             this.indicator.removeClass('visible');
30224             this.indicator.addClass('invisible');
30225         }
30226         
30227         this.el.removeClass(this.invalidClass);
30228         
30229         this.el.addClass(this.validClass);
30230         
30231         this.fireEvent('valid', this);
30232     },
30233     
30234     /**
30235      * Mark this field as invalid
30236      * @param {String} msg The validation message
30237      */
30238     markInvalid : function(msg)
30239     {
30240         if(this.indicator){
30241             this.indicator.removeClass('invisible');
30242             this.indicator.addClass('visible');
30243         }
30244         
30245         this.el.removeClass(this.validClass);
30246         
30247         this.el.addClass(this.invalidClass);
30248         
30249         this.fireEvent('invalid', this, msg);
30250     }
30251     
30252    
30253 });
30254
30255 Roo.apply(Roo.bootstrap.FieldLabel, {
30256     
30257     groups: {},
30258     
30259      /**
30260     * register a FieldLabel Group
30261     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30262     */
30263     register : function(label)
30264     {
30265         if(this.groups.hasOwnProperty(label.target)){
30266             return;
30267         }
30268      
30269         this.groups[label.target] = label;
30270         
30271     },
30272     /**
30273     * fetch a FieldLabel Group based on the target
30274     * @param {string} target
30275     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30276     */
30277     get: function(target) {
30278         if (typeof(this.groups[target]) == 'undefined') {
30279             return false;
30280         }
30281         
30282         return this.groups[target] ;
30283     }
30284 });
30285
30286  
30287
30288  /*
30289  * - LGPL
30290  *
30291  * page DateSplitField.
30292  * 
30293  */
30294
30295
30296 /**
30297  * @class Roo.bootstrap.DateSplitField
30298  * @extends Roo.bootstrap.Component
30299  * Bootstrap DateSplitField class
30300  * @cfg {string} fieldLabel - the label associated
30301  * @cfg {Number} labelWidth set the width of label (0-12)
30302  * @cfg {String} labelAlign (top|left)
30303  * @cfg {Boolean} dayAllowBlank (true|false) default false
30304  * @cfg {Boolean} monthAllowBlank (true|false) default false
30305  * @cfg {Boolean} yearAllowBlank (true|false) default false
30306  * @cfg {string} dayPlaceholder 
30307  * @cfg {string} monthPlaceholder
30308  * @cfg {string} yearPlaceholder
30309  * @cfg {string} dayFormat default 'd'
30310  * @cfg {string} monthFormat default 'm'
30311  * @cfg {string} yearFormat default 'Y'
30312  * @cfg {Number} labellg set the width of label (1-12)
30313  * @cfg {Number} labelmd set the width of label (1-12)
30314  * @cfg {Number} labelsm set the width of label (1-12)
30315  * @cfg {Number} labelxs set the width of label (1-12)
30316
30317  *     
30318  * @constructor
30319  * Create a new DateSplitField
30320  * @param {Object} config The config object
30321  */
30322
30323 Roo.bootstrap.DateSplitField = function(config){
30324     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30325     
30326     this.addEvents({
30327         // raw events
30328          /**
30329          * @event years
30330          * getting the data of years
30331          * @param {Roo.bootstrap.DateSplitField} this
30332          * @param {Object} years
30333          */
30334         "years" : true,
30335         /**
30336          * @event days
30337          * getting the data of days
30338          * @param {Roo.bootstrap.DateSplitField} this
30339          * @param {Object} days
30340          */
30341         "days" : true,
30342         /**
30343          * @event invalid
30344          * Fires after the field has been marked as invalid.
30345          * @param {Roo.form.Field} this
30346          * @param {String} msg The validation message
30347          */
30348         invalid : true,
30349        /**
30350          * @event valid
30351          * Fires after the field has been validated with no errors.
30352          * @param {Roo.form.Field} this
30353          */
30354         valid : true
30355     });
30356 };
30357
30358 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30359     
30360     fieldLabel : '',
30361     labelAlign : 'top',
30362     labelWidth : 3,
30363     dayAllowBlank : false,
30364     monthAllowBlank : false,
30365     yearAllowBlank : false,
30366     dayPlaceholder : '',
30367     monthPlaceholder : '',
30368     yearPlaceholder : '',
30369     dayFormat : 'd',
30370     monthFormat : 'm',
30371     yearFormat : 'Y',
30372     isFormField : true,
30373     labellg : 0,
30374     labelmd : 0,
30375     labelsm : 0,
30376     labelxs : 0,
30377     
30378     getAutoCreate : function()
30379     {
30380         var cfg = {
30381             tag : 'div',
30382             cls : 'row roo-date-split-field-group',
30383             cn : [
30384                 {
30385                     tag : 'input',
30386                     type : 'hidden',
30387                     cls : 'form-hidden-field roo-date-split-field-group-value',
30388                     name : this.name
30389                 }
30390             ]
30391         };
30392         
30393         var labelCls = 'col-md-12';
30394         var contentCls = 'col-md-4';
30395         
30396         if(this.fieldLabel){
30397             
30398             var label = {
30399                 tag : 'div',
30400                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30401                 cn : [
30402                     {
30403                         tag : 'label',
30404                         html : this.fieldLabel
30405                     }
30406                 ]
30407             };
30408             
30409             if(this.labelAlign == 'left'){
30410             
30411                 if(this.labelWidth > 12){
30412                     label.style = "width: " + this.labelWidth + 'px';
30413                 }
30414
30415                 if(this.labelWidth < 13 && this.labelmd == 0){
30416                     this.labelmd = this.labelWidth;
30417                 }
30418
30419                 if(this.labellg > 0){
30420                     labelCls = ' col-lg-' + this.labellg;
30421                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30422                 }
30423
30424                 if(this.labelmd > 0){
30425                     labelCls = ' col-md-' + this.labelmd;
30426                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30427                 }
30428
30429                 if(this.labelsm > 0){
30430                     labelCls = ' col-sm-' + this.labelsm;
30431                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30432                 }
30433
30434                 if(this.labelxs > 0){
30435                     labelCls = ' col-xs-' + this.labelxs;
30436                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30437                 }
30438             }
30439             
30440             label.cls += ' ' + labelCls;
30441             
30442             cfg.cn.push(label);
30443         }
30444         
30445         Roo.each(['day', 'month', 'year'], function(t){
30446             cfg.cn.push({
30447                 tag : 'div',
30448                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30449             });
30450         }, this);
30451         
30452         return cfg;
30453     },
30454     
30455     inputEl: function ()
30456     {
30457         return this.el.select('.roo-date-split-field-group-value', true).first();
30458     },
30459     
30460     onRender : function(ct, position) 
30461     {
30462         var _this = this;
30463         
30464         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30465         
30466         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30467         
30468         this.dayField = new Roo.bootstrap.ComboBox({
30469             allowBlank : this.dayAllowBlank,
30470             alwaysQuery : true,
30471             displayField : 'value',
30472             editable : false,
30473             fieldLabel : '',
30474             forceSelection : true,
30475             mode : 'local',
30476             placeholder : this.dayPlaceholder,
30477             selectOnFocus : true,
30478             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30479             triggerAction : 'all',
30480             typeAhead : true,
30481             valueField : 'value',
30482             store : new Roo.data.SimpleStore({
30483                 data : (function() {    
30484                     var days = [];
30485                     _this.fireEvent('days', _this, days);
30486                     return days;
30487                 })(),
30488                 fields : [ 'value' ]
30489             }),
30490             listeners : {
30491                 select : function (_self, record, index)
30492                 {
30493                     _this.setValue(_this.getValue());
30494                 }
30495             }
30496         });
30497
30498         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30499         
30500         this.monthField = new Roo.bootstrap.MonthField({
30501             after : '<i class=\"fa fa-calendar\"></i>',
30502             allowBlank : this.monthAllowBlank,
30503             placeholder : this.monthPlaceholder,
30504             readOnly : true,
30505             listeners : {
30506                 render : function (_self)
30507                 {
30508                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30509                         e.preventDefault();
30510                         _self.focus();
30511                     });
30512                 },
30513                 select : function (_self, oldvalue, newvalue)
30514                 {
30515                     _this.setValue(_this.getValue());
30516                 }
30517             }
30518         });
30519         
30520         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30521         
30522         this.yearField = new Roo.bootstrap.ComboBox({
30523             allowBlank : this.yearAllowBlank,
30524             alwaysQuery : true,
30525             displayField : 'value',
30526             editable : false,
30527             fieldLabel : '',
30528             forceSelection : true,
30529             mode : 'local',
30530             placeholder : this.yearPlaceholder,
30531             selectOnFocus : true,
30532             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30533             triggerAction : 'all',
30534             typeAhead : true,
30535             valueField : 'value',
30536             store : new Roo.data.SimpleStore({
30537                 data : (function() {
30538                     var years = [];
30539                     _this.fireEvent('years', _this, years);
30540                     return years;
30541                 })(),
30542                 fields : [ 'value' ]
30543             }),
30544             listeners : {
30545                 select : function (_self, record, index)
30546                 {
30547                     _this.setValue(_this.getValue());
30548                 }
30549             }
30550         });
30551
30552         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30553     },
30554     
30555     setValue : function(v, format)
30556     {
30557         this.inputEl.dom.value = v;
30558         
30559         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30560         
30561         var d = Date.parseDate(v, f);
30562         
30563         if(!d){
30564             this.validate();
30565             return;
30566         }
30567         
30568         this.setDay(d.format(this.dayFormat));
30569         this.setMonth(d.format(this.monthFormat));
30570         this.setYear(d.format(this.yearFormat));
30571         
30572         this.validate();
30573         
30574         return;
30575     },
30576     
30577     setDay : function(v)
30578     {
30579         this.dayField.setValue(v);
30580         this.inputEl.dom.value = this.getValue();
30581         this.validate();
30582         return;
30583     },
30584     
30585     setMonth : function(v)
30586     {
30587         this.monthField.setValue(v, true);
30588         this.inputEl.dom.value = this.getValue();
30589         this.validate();
30590         return;
30591     },
30592     
30593     setYear : function(v)
30594     {
30595         this.yearField.setValue(v);
30596         this.inputEl.dom.value = this.getValue();
30597         this.validate();
30598         return;
30599     },
30600     
30601     getDay : function()
30602     {
30603         return this.dayField.getValue();
30604     },
30605     
30606     getMonth : function()
30607     {
30608         return this.monthField.getValue();
30609     },
30610     
30611     getYear : function()
30612     {
30613         return this.yearField.getValue();
30614     },
30615     
30616     getValue : function()
30617     {
30618         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30619         
30620         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30621         
30622         return date;
30623     },
30624     
30625     reset : function()
30626     {
30627         this.setDay('');
30628         this.setMonth('');
30629         this.setYear('');
30630         this.inputEl.dom.value = '';
30631         this.validate();
30632         return;
30633     },
30634     
30635     validate : function()
30636     {
30637         var d = this.dayField.validate();
30638         var m = this.monthField.validate();
30639         var y = this.yearField.validate();
30640         
30641         var valid = true;
30642         
30643         if(
30644                 (!this.dayAllowBlank && !d) ||
30645                 (!this.monthAllowBlank && !m) ||
30646                 (!this.yearAllowBlank && !y)
30647         ){
30648             valid = false;
30649         }
30650         
30651         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30652             return valid;
30653         }
30654         
30655         if(valid){
30656             this.markValid();
30657             return valid;
30658         }
30659         
30660         this.markInvalid();
30661         
30662         return valid;
30663     },
30664     
30665     markValid : function()
30666     {
30667         
30668         var label = this.el.select('label', true).first();
30669         var icon = this.el.select('i.fa-star', true).first();
30670
30671         if(label && icon){
30672             icon.remove();
30673         }
30674         
30675         this.fireEvent('valid', this);
30676     },
30677     
30678      /**
30679      * Mark this field as invalid
30680      * @param {String} msg The validation message
30681      */
30682     markInvalid : function(msg)
30683     {
30684         
30685         var label = this.el.select('label', true).first();
30686         var icon = this.el.select('i.fa-star', true).first();
30687
30688         if(label && !icon){
30689             this.el.select('.roo-date-split-field-label', true).createChild({
30690                 tag : 'i',
30691                 cls : 'text-danger fa fa-lg fa-star',
30692                 tooltip : 'This field is required',
30693                 style : 'margin-right:5px;'
30694             }, label, true);
30695         }
30696         
30697         this.fireEvent('invalid', this, msg);
30698     },
30699     
30700     clearInvalid : function()
30701     {
30702         var label = this.el.select('label', true).first();
30703         var icon = this.el.select('i.fa-star', true).first();
30704
30705         if(label && icon){
30706             icon.remove();
30707         }
30708         
30709         this.fireEvent('valid', this);
30710     },
30711     
30712     getName: function()
30713     {
30714         return this.name;
30715     }
30716     
30717 });
30718
30719  /**
30720  *
30721  * This is based on 
30722  * http://masonry.desandro.com
30723  *
30724  * The idea is to render all the bricks based on vertical width...
30725  *
30726  * The original code extends 'outlayer' - we might need to use that....
30727  * 
30728  */
30729
30730
30731 /**
30732  * @class Roo.bootstrap.LayoutMasonry
30733  * @extends Roo.bootstrap.Component
30734  * Bootstrap Layout Masonry class
30735  * 
30736  * @constructor
30737  * Create a new Element
30738  * @param {Object} config The config object
30739  */
30740
30741 Roo.bootstrap.LayoutMasonry = function(config){
30742     
30743     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30744     
30745     this.bricks = [];
30746     
30747     Roo.bootstrap.LayoutMasonry.register(this);
30748     
30749     this.addEvents({
30750         // raw events
30751         /**
30752          * @event layout
30753          * Fire after layout the items
30754          * @param {Roo.bootstrap.LayoutMasonry} this
30755          * @param {Roo.EventObject} e
30756          */
30757         "layout" : true
30758     });
30759     
30760 };
30761
30762 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30763     
30764     /**
30765      * @cfg {Boolean} isLayoutInstant = no animation?
30766      */   
30767     isLayoutInstant : false, // needed?
30768    
30769     /**
30770      * @cfg {Number} boxWidth  width of the columns
30771      */   
30772     boxWidth : 450,
30773     
30774       /**
30775      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30776      */   
30777     boxHeight : 0,
30778     
30779     /**
30780      * @cfg {Number} padWidth padding below box..
30781      */   
30782     padWidth : 10, 
30783     
30784     /**
30785      * @cfg {Number} gutter gutter width..
30786      */   
30787     gutter : 10,
30788     
30789      /**
30790      * @cfg {Number} maxCols maximum number of columns
30791      */   
30792     
30793     maxCols: 0,
30794     
30795     /**
30796      * @cfg {Boolean} isAutoInitial defalut true
30797      */   
30798     isAutoInitial : true, 
30799     
30800     containerWidth: 0,
30801     
30802     /**
30803      * @cfg {Boolean} isHorizontal defalut false
30804      */   
30805     isHorizontal : false, 
30806
30807     currentSize : null,
30808     
30809     tag: 'div',
30810     
30811     cls: '',
30812     
30813     bricks: null, //CompositeElement
30814     
30815     cols : 1,
30816     
30817     _isLayoutInited : false,
30818     
30819 //    isAlternative : false, // only use for vertical layout...
30820     
30821     /**
30822      * @cfg {Number} alternativePadWidth padding below box..
30823      */   
30824     alternativePadWidth : 50,
30825     
30826     selectedBrick : [],
30827     
30828     getAutoCreate : function(){
30829         
30830         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30831         
30832         var cfg = {
30833             tag: this.tag,
30834             cls: 'blog-masonary-wrapper ' + this.cls,
30835             cn : {
30836                 cls : 'mas-boxes masonary'
30837             }
30838         };
30839         
30840         return cfg;
30841     },
30842     
30843     getChildContainer: function( )
30844     {
30845         if (this.boxesEl) {
30846             return this.boxesEl;
30847         }
30848         
30849         this.boxesEl = this.el.select('.mas-boxes').first();
30850         
30851         return this.boxesEl;
30852     },
30853     
30854     
30855     initEvents : function()
30856     {
30857         var _this = this;
30858         
30859         if(this.isAutoInitial){
30860             Roo.log('hook children rendered');
30861             this.on('childrenrendered', function() {
30862                 Roo.log('children rendered');
30863                 _this.initial();
30864             } ,this);
30865         }
30866     },
30867     
30868     initial : function()
30869     {
30870         this.selectedBrick = [];
30871         
30872         this.currentSize = this.el.getBox(true);
30873         
30874         Roo.EventManager.onWindowResize(this.resize, this); 
30875
30876         if(!this.isAutoInitial){
30877             this.layout();
30878             return;
30879         }
30880         
30881         this.layout();
30882         
30883         return;
30884         //this.layout.defer(500,this);
30885         
30886     },
30887     
30888     resize : function()
30889     {
30890         var cs = this.el.getBox(true);
30891         
30892         if (
30893                 this.currentSize.width == cs.width && 
30894                 this.currentSize.x == cs.x && 
30895                 this.currentSize.height == cs.height && 
30896                 this.currentSize.y == cs.y 
30897         ) {
30898             Roo.log("no change in with or X or Y");
30899             return;
30900         }
30901         
30902         this.currentSize = cs;
30903         
30904         this.layout();
30905         
30906     },
30907     
30908     layout : function()
30909     {   
30910         this._resetLayout();
30911         
30912         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30913         
30914         this.layoutItems( isInstant );
30915       
30916         this._isLayoutInited = true;
30917         
30918         this.fireEvent('layout', this);
30919         
30920     },
30921     
30922     _resetLayout : function()
30923     {
30924         if(this.isHorizontal){
30925             this.horizontalMeasureColumns();
30926             return;
30927         }
30928         
30929         this.verticalMeasureColumns();
30930         
30931     },
30932     
30933     verticalMeasureColumns : function()
30934     {
30935         this.getContainerWidth();
30936         
30937 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30938 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30939 //            return;
30940 //        }
30941         
30942         var boxWidth = this.boxWidth + this.padWidth;
30943         
30944         if(this.containerWidth < this.boxWidth){
30945             boxWidth = this.containerWidth
30946         }
30947         
30948         var containerWidth = this.containerWidth;
30949         
30950         var cols = Math.floor(containerWidth / boxWidth);
30951         
30952         this.cols = Math.max( cols, 1 );
30953         
30954         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30955         
30956         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30957         
30958         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30959         
30960         this.colWidth = boxWidth + avail - this.padWidth;
30961         
30962         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30963         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30964     },
30965     
30966     horizontalMeasureColumns : function()
30967     {
30968         this.getContainerWidth();
30969         
30970         var boxWidth = this.boxWidth;
30971         
30972         if(this.containerWidth < boxWidth){
30973             boxWidth = this.containerWidth;
30974         }
30975         
30976         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30977         
30978         this.el.setHeight(boxWidth);
30979         
30980     },
30981     
30982     getContainerWidth : function()
30983     {
30984         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30985     },
30986     
30987     layoutItems : function( isInstant )
30988     {
30989         Roo.log(this.bricks);
30990         
30991         var items = Roo.apply([], this.bricks);
30992         
30993         if(this.isHorizontal){
30994             this._horizontalLayoutItems( items , isInstant );
30995             return;
30996         }
30997         
30998 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30999 //            this._verticalAlternativeLayoutItems( items , isInstant );
31000 //            return;
31001 //        }
31002         
31003         this._verticalLayoutItems( items , isInstant );
31004         
31005     },
31006     
31007     _verticalLayoutItems : function ( items , isInstant)
31008     {
31009         if ( !items || !items.length ) {
31010             return;
31011         }
31012         
31013         var standard = [
31014             ['xs', 'xs', 'xs', 'tall'],
31015             ['xs', 'xs', 'tall'],
31016             ['xs', 'xs', 'sm'],
31017             ['xs', 'xs', 'xs'],
31018             ['xs', 'tall'],
31019             ['xs', 'sm'],
31020             ['xs', 'xs'],
31021             ['xs'],
31022             
31023             ['sm', 'xs', 'xs'],
31024             ['sm', 'xs'],
31025             ['sm'],
31026             
31027             ['tall', 'xs', 'xs', 'xs'],
31028             ['tall', 'xs', 'xs'],
31029             ['tall', 'xs'],
31030             ['tall']
31031             
31032         ];
31033         
31034         var queue = [];
31035         
31036         var boxes = [];
31037         
31038         var box = [];
31039         
31040         Roo.each(items, function(item, k){
31041             
31042             switch (item.size) {
31043                 // these layouts take up a full box,
31044                 case 'md' :
31045                 case 'md-left' :
31046                 case 'md-right' :
31047                 case 'wide' :
31048                     
31049                     if(box.length){
31050                         boxes.push(box);
31051                         box = [];
31052                     }
31053                     
31054                     boxes.push([item]);
31055                     
31056                     break;
31057                     
31058                 case 'xs' :
31059                 case 'sm' :
31060                 case 'tall' :
31061                     
31062                     box.push(item);
31063                     
31064                     break;
31065                 default :
31066                     break;
31067                     
31068             }
31069             
31070         }, this);
31071         
31072         if(box.length){
31073             boxes.push(box);
31074             box = [];
31075         }
31076         
31077         var filterPattern = function(box, length)
31078         {
31079             if(!box.length){
31080                 return;
31081             }
31082             
31083             var match = false;
31084             
31085             var pattern = box.slice(0, length);
31086             
31087             var format = [];
31088             
31089             Roo.each(pattern, function(i){
31090                 format.push(i.size);
31091             }, this);
31092             
31093             Roo.each(standard, function(s){
31094                 
31095                 if(String(s) != String(format)){
31096                     return;
31097                 }
31098                 
31099                 match = true;
31100                 return false;
31101                 
31102             }, this);
31103             
31104             if(!match && length == 1){
31105                 return;
31106             }
31107             
31108             if(!match){
31109                 filterPattern(box, length - 1);
31110                 return;
31111             }
31112                 
31113             queue.push(pattern);
31114
31115             box = box.slice(length, box.length);
31116
31117             filterPattern(box, 4);
31118
31119             return;
31120             
31121         }
31122         
31123         Roo.each(boxes, function(box, k){
31124             
31125             if(!box.length){
31126                 return;
31127             }
31128             
31129             if(box.length == 1){
31130                 queue.push(box);
31131                 return;
31132             }
31133             
31134             filterPattern(box, 4);
31135             
31136         }, this);
31137         
31138         this._processVerticalLayoutQueue( queue, isInstant );
31139         
31140     },
31141     
31142 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31143 //    {
31144 //        if ( !items || !items.length ) {
31145 //            return;
31146 //        }
31147 //
31148 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31149 //        
31150 //    },
31151     
31152     _horizontalLayoutItems : function ( items , isInstant)
31153     {
31154         if ( !items || !items.length || items.length < 3) {
31155             return;
31156         }
31157         
31158         items.reverse();
31159         
31160         var eItems = items.slice(0, 3);
31161         
31162         items = items.slice(3, items.length);
31163         
31164         var standard = [
31165             ['xs', 'xs', 'xs', 'wide'],
31166             ['xs', 'xs', 'wide'],
31167             ['xs', 'xs', 'sm'],
31168             ['xs', 'xs', 'xs'],
31169             ['xs', 'wide'],
31170             ['xs', 'sm'],
31171             ['xs', 'xs'],
31172             ['xs'],
31173             
31174             ['sm', 'xs', 'xs'],
31175             ['sm', 'xs'],
31176             ['sm'],
31177             
31178             ['wide', 'xs', 'xs', 'xs'],
31179             ['wide', 'xs', 'xs'],
31180             ['wide', 'xs'],
31181             ['wide'],
31182             
31183             ['wide-thin']
31184         ];
31185         
31186         var queue = [];
31187         
31188         var boxes = [];
31189         
31190         var box = [];
31191         
31192         Roo.each(items, function(item, k){
31193             
31194             switch (item.size) {
31195                 case 'md' :
31196                 case 'md-left' :
31197                 case 'md-right' :
31198                 case 'tall' :
31199                     
31200                     if(box.length){
31201                         boxes.push(box);
31202                         box = [];
31203                     }
31204                     
31205                     boxes.push([item]);
31206                     
31207                     break;
31208                     
31209                 case 'xs' :
31210                 case 'sm' :
31211                 case 'wide' :
31212                 case 'wide-thin' :
31213                     
31214                     box.push(item);
31215                     
31216                     break;
31217                 default :
31218                     break;
31219                     
31220             }
31221             
31222         }, this);
31223         
31224         if(box.length){
31225             boxes.push(box);
31226             box = [];
31227         }
31228         
31229         var filterPattern = function(box, length)
31230         {
31231             if(!box.length){
31232                 return;
31233             }
31234             
31235             var match = false;
31236             
31237             var pattern = box.slice(0, length);
31238             
31239             var format = [];
31240             
31241             Roo.each(pattern, function(i){
31242                 format.push(i.size);
31243             }, this);
31244             
31245             Roo.each(standard, function(s){
31246                 
31247                 if(String(s) != String(format)){
31248                     return;
31249                 }
31250                 
31251                 match = true;
31252                 return false;
31253                 
31254             }, this);
31255             
31256             if(!match && length == 1){
31257                 return;
31258             }
31259             
31260             if(!match){
31261                 filterPattern(box, length - 1);
31262                 return;
31263             }
31264                 
31265             queue.push(pattern);
31266
31267             box = box.slice(length, box.length);
31268
31269             filterPattern(box, 4);
31270
31271             return;
31272             
31273         }
31274         
31275         Roo.each(boxes, function(box, k){
31276             
31277             if(!box.length){
31278                 return;
31279             }
31280             
31281             if(box.length == 1){
31282                 queue.push(box);
31283                 return;
31284             }
31285             
31286             filterPattern(box, 4);
31287             
31288         }, this);
31289         
31290         
31291         var prune = [];
31292         
31293         var pos = this.el.getBox(true);
31294         
31295         var minX = pos.x;
31296         
31297         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31298         
31299         var hit_end = false;
31300         
31301         Roo.each(queue, function(box){
31302             
31303             if(hit_end){
31304                 
31305                 Roo.each(box, function(b){
31306                 
31307                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31308                     b.el.hide();
31309
31310                 }, this);
31311
31312                 return;
31313             }
31314             
31315             var mx = 0;
31316             
31317             Roo.each(box, function(b){
31318                 
31319                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31320                 b.el.show();
31321
31322                 mx = Math.max(mx, b.x);
31323                 
31324             }, this);
31325             
31326             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31327             
31328             if(maxX < minX){
31329                 
31330                 Roo.each(box, function(b){
31331                 
31332                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31333                     b.el.hide();
31334                     
31335                 }, this);
31336                 
31337                 hit_end = true;
31338                 
31339                 return;
31340             }
31341             
31342             prune.push(box);
31343             
31344         }, this);
31345         
31346         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31347     },
31348     
31349     /** Sets position of item in DOM
31350     * @param {Element} item
31351     * @param {Number} x - horizontal position
31352     * @param {Number} y - vertical position
31353     * @param {Boolean} isInstant - disables transitions
31354     */
31355     _processVerticalLayoutQueue : function( queue, isInstant )
31356     {
31357         var pos = this.el.getBox(true);
31358         var x = pos.x;
31359         var y = pos.y;
31360         var maxY = [];
31361         
31362         for (var i = 0; i < this.cols; i++){
31363             maxY[i] = pos.y;
31364         }
31365         
31366         Roo.each(queue, function(box, k){
31367             
31368             var col = k % this.cols;
31369             
31370             Roo.each(box, function(b,kk){
31371                 
31372                 b.el.position('absolute');
31373                 
31374                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31375                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31376                 
31377                 if(b.size == 'md-left' || b.size == 'md-right'){
31378                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31379                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31380                 }
31381                 
31382                 b.el.setWidth(width);
31383                 b.el.setHeight(height);
31384                 // iframe?
31385                 b.el.select('iframe',true).setSize(width,height);
31386                 
31387             }, this);
31388             
31389             for (var i = 0; i < this.cols; i++){
31390                 
31391                 if(maxY[i] < maxY[col]){
31392                     col = i;
31393                     continue;
31394                 }
31395                 
31396                 col = Math.min(col, i);
31397                 
31398             }
31399             
31400             x = pos.x + col * (this.colWidth + this.padWidth);
31401             
31402             y = maxY[col];
31403             
31404             var positions = [];
31405             
31406             switch (box.length){
31407                 case 1 :
31408                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31409                     break;
31410                 case 2 :
31411                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31412                     break;
31413                 case 3 :
31414                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31415                     break;
31416                 case 4 :
31417                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31418                     break;
31419                 default :
31420                     break;
31421             }
31422             
31423             Roo.each(box, function(b,kk){
31424                 
31425                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31426                 
31427                 var sz = b.el.getSize();
31428                 
31429                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31430                 
31431             }, this);
31432             
31433         }, this);
31434         
31435         var mY = 0;
31436         
31437         for (var i = 0; i < this.cols; i++){
31438             mY = Math.max(mY, maxY[i]);
31439         }
31440         
31441         this.el.setHeight(mY - pos.y);
31442         
31443     },
31444     
31445 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31446 //    {
31447 //        var pos = this.el.getBox(true);
31448 //        var x = pos.x;
31449 //        var y = pos.y;
31450 //        var maxX = pos.right;
31451 //        
31452 //        var maxHeight = 0;
31453 //        
31454 //        Roo.each(items, function(item, k){
31455 //            
31456 //            var c = k % 2;
31457 //            
31458 //            item.el.position('absolute');
31459 //                
31460 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31461 //
31462 //            item.el.setWidth(width);
31463 //
31464 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31465 //
31466 //            item.el.setHeight(height);
31467 //            
31468 //            if(c == 0){
31469 //                item.el.setXY([x, y], isInstant ? false : true);
31470 //            } else {
31471 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31472 //            }
31473 //            
31474 //            y = y + height + this.alternativePadWidth;
31475 //            
31476 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31477 //            
31478 //        }, this);
31479 //        
31480 //        this.el.setHeight(maxHeight);
31481 //        
31482 //    },
31483     
31484     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31485     {
31486         var pos = this.el.getBox(true);
31487         
31488         var minX = pos.x;
31489         var minY = pos.y;
31490         
31491         var maxX = pos.right;
31492         
31493         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31494         
31495         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31496         
31497         Roo.each(queue, function(box, k){
31498             
31499             Roo.each(box, function(b, kk){
31500                 
31501                 b.el.position('absolute');
31502                 
31503                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31504                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31505                 
31506                 if(b.size == 'md-left' || b.size == 'md-right'){
31507                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31508                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31509                 }
31510                 
31511                 b.el.setWidth(width);
31512                 b.el.setHeight(height);
31513                 
31514             }, this);
31515             
31516             if(!box.length){
31517                 return;
31518             }
31519             
31520             var positions = [];
31521             
31522             switch (box.length){
31523                 case 1 :
31524                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31525                     break;
31526                 case 2 :
31527                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31528                     break;
31529                 case 3 :
31530                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31531                     break;
31532                 case 4 :
31533                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31534                     break;
31535                 default :
31536                     break;
31537             }
31538             
31539             Roo.each(box, function(b,kk){
31540                 
31541                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31542                 
31543                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31544                 
31545             }, this);
31546             
31547         }, this);
31548         
31549     },
31550     
31551     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31552     {
31553         Roo.each(eItems, function(b,k){
31554             
31555             b.size = (k == 0) ? 'sm' : 'xs';
31556             b.x = (k == 0) ? 2 : 1;
31557             b.y = (k == 0) ? 2 : 1;
31558             
31559             b.el.position('absolute');
31560             
31561             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31562                 
31563             b.el.setWidth(width);
31564             
31565             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31566             
31567             b.el.setHeight(height);
31568             
31569         }, this);
31570
31571         var positions = [];
31572         
31573         positions.push({
31574             x : maxX - this.unitWidth * 2 - this.gutter,
31575             y : minY
31576         });
31577         
31578         positions.push({
31579             x : maxX - this.unitWidth,
31580             y : minY + (this.unitWidth + this.gutter) * 2
31581         });
31582         
31583         positions.push({
31584             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31585             y : minY
31586         });
31587         
31588         Roo.each(eItems, function(b,k){
31589             
31590             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31591
31592         }, this);
31593         
31594     },
31595     
31596     getVerticalOneBoxColPositions : function(x, y, box)
31597     {
31598         var pos = [];
31599         
31600         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31601         
31602         if(box[0].size == 'md-left'){
31603             rand = 0;
31604         }
31605         
31606         if(box[0].size == 'md-right'){
31607             rand = 1;
31608         }
31609         
31610         pos.push({
31611             x : x + (this.unitWidth + this.gutter) * rand,
31612             y : y
31613         });
31614         
31615         return pos;
31616     },
31617     
31618     getVerticalTwoBoxColPositions : function(x, y, box)
31619     {
31620         var pos = [];
31621         
31622         if(box[0].size == 'xs'){
31623             
31624             pos.push({
31625                 x : x,
31626                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31627             });
31628
31629             pos.push({
31630                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31631                 y : y
31632             });
31633             
31634             return pos;
31635             
31636         }
31637         
31638         pos.push({
31639             x : x,
31640             y : y
31641         });
31642
31643         pos.push({
31644             x : x + (this.unitWidth + this.gutter) * 2,
31645             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31646         });
31647         
31648         return pos;
31649         
31650     },
31651     
31652     getVerticalThreeBoxColPositions : function(x, y, box)
31653     {
31654         var pos = [];
31655         
31656         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31657             
31658             pos.push({
31659                 x : x,
31660                 y : y
31661             });
31662
31663             pos.push({
31664                 x : x + (this.unitWidth + this.gutter) * 1,
31665                 y : y
31666             });
31667             
31668             pos.push({
31669                 x : x + (this.unitWidth + this.gutter) * 2,
31670                 y : y
31671             });
31672             
31673             return pos;
31674             
31675         }
31676         
31677         if(box[0].size == 'xs' && box[1].size == 'xs'){
31678             
31679             pos.push({
31680                 x : x,
31681                 y : y
31682             });
31683
31684             pos.push({
31685                 x : x,
31686                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31687             });
31688             
31689             pos.push({
31690                 x : x + (this.unitWidth + this.gutter) * 1,
31691                 y : y
31692             });
31693             
31694             return pos;
31695             
31696         }
31697         
31698         pos.push({
31699             x : x,
31700             y : y
31701         });
31702
31703         pos.push({
31704             x : x + (this.unitWidth + this.gutter) * 2,
31705             y : y
31706         });
31707
31708         pos.push({
31709             x : x + (this.unitWidth + this.gutter) * 2,
31710             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31711         });
31712             
31713         return pos;
31714         
31715     },
31716     
31717     getVerticalFourBoxColPositions : function(x, y, box)
31718     {
31719         var pos = [];
31720         
31721         if(box[0].size == 'xs'){
31722             
31723             pos.push({
31724                 x : x,
31725                 y : y
31726             });
31727
31728             pos.push({
31729                 x : x,
31730                 y : y + (this.unitHeight + this.gutter) * 1
31731             });
31732             
31733             pos.push({
31734                 x : x,
31735                 y : y + (this.unitHeight + this.gutter) * 2
31736             });
31737             
31738             pos.push({
31739                 x : x + (this.unitWidth + this.gutter) * 1,
31740                 y : y
31741             });
31742             
31743             return pos;
31744             
31745         }
31746         
31747         pos.push({
31748             x : x,
31749             y : y
31750         });
31751
31752         pos.push({
31753             x : x + (this.unitWidth + this.gutter) * 2,
31754             y : y
31755         });
31756
31757         pos.push({
31758             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31759             y : y + (this.unitHeight + this.gutter) * 1
31760         });
31761
31762         pos.push({
31763             x : x + (this.unitWidth + this.gutter) * 2,
31764             y : y + (this.unitWidth + this.gutter) * 2
31765         });
31766
31767         return pos;
31768         
31769     },
31770     
31771     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31772     {
31773         var pos = [];
31774         
31775         if(box[0].size == 'md-left'){
31776             pos.push({
31777                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31778                 y : minY
31779             });
31780             
31781             return pos;
31782         }
31783         
31784         if(box[0].size == 'md-right'){
31785             pos.push({
31786                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31787                 y : minY + (this.unitWidth + this.gutter) * 1
31788             });
31789             
31790             return pos;
31791         }
31792         
31793         var rand = Math.floor(Math.random() * (4 - box[0].y));
31794         
31795         pos.push({
31796             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31797             y : minY + (this.unitWidth + this.gutter) * rand
31798         });
31799         
31800         return pos;
31801         
31802     },
31803     
31804     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31805     {
31806         var pos = [];
31807         
31808         if(box[0].size == 'xs'){
31809             
31810             pos.push({
31811                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31812                 y : minY
31813             });
31814
31815             pos.push({
31816                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31817                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31818             });
31819             
31820             return pos;
31821             
31822         }
31823         
31824         pos.push({
31825             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31826             y : minY
31827         });
31828
31829         pos.push({
31830             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31831             y : minY + (this.unitWidth + this.gutter) * 2
31832         });
31833         
31834         return pos;
31835         
31836     },
31837     
31838     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31839     {
31840         var pos = [];
31841         
31842         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31843             
31844             pos.push({
31845                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31846                 y : minY
31847             });
31848
31849             pos.push({
31850                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31851                 y : minY + (this.unitWidth + this.gutter) * 1
31852             });
31853             
31854             pos.push({
31855                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31856                 y : minY + (this.unitWidth + this.gutter) * 2
31857             });
31858             
31859             return pos;
31860             
31861         }
31862         
31863         if(box[0].size == 'xs' && box[1].size == 'xs'){
31864             
31865             pos.push({
31866                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31867                 y : minY
31868             });
31869
31870             pos.push({
31871                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31872                 y : minY
31873             });
31874             
31875             pos.push({
31876                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31877                 y : minY + (this.unitWidth + this.gutter) * 1
31878             });
31879             
31880             return pos;
31881             
31882         }
31883         
31884         pos.push({
31885             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31886             y : minY
31887         });
31888
31889         pos.push({
31890             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31891             y : minY + (this.unitWidth + this.gutter) * 2
31892         });
31893
31894         pos.push({
31895             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31896             y : minY + (this.unitWidth + this.gutter) * 2
31897         });
31898             
31899         return pos;
31900         
31901     },
31902     
31903     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31904     {
31905         var pos = [];
31906         
31907         if(box[0].size == 'xs'){
31908             
31909             pos.push({
31910                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31911                 y : minY
31912             });
31913
31914             pos.push({
31915                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31916                 y : minY
31917             });
31918             
31919             pos.push({
31920                 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),
31921                 y : minY
31922             });
31923             
31924             pos.push({
31925                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31926                 y : minY + (this.unitWidth + this.gutter) * 1
31927             });
31928             
31929             return pos;
31930             
31931         }
31932         
31933         pos.push({
31934             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31935             y : minY
31936         });
31937         
31938         pos.push({
31939             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31940             y : minY + (this.unitWidth + this.gutter) * 2
31941         });
31942         
31943         pos.push({
31944             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31945             y : minY + (this.unitWidth + this.gutter) * 2
31946         });
31947         
31948         pos.push({
31949             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),
31950             y : minY + (this.unitWidth + this.gutter) * 2
31951         });
31952
31953         return pos;
31954         
31955     },
31956     
31957     /**
31958     * remove a Masonry Brick
31959     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31960     */
31961     removeBrick : function(brick_id)
31962     {
31963         if (!brick_id) {
31964             return;
31965         }
31966         
31967         for (var i = 0; i<this.bricks.length; i++) {
31968             if (this.bricks[i].id == brick_id) {
31969                 this.bricks.splice(i,1);
31970                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31971                 this.initial();
31972             }
31973         }
31974     },
31975     
31976     /**
31977     * adds a Masonry Brick
31978     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31979     */
31980     addBrick : function(cfg)
31981     {
31982         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31983         //this.register(cn);
31984         cn.parentId = this.id;
31985         cn.render(this.el);
31986         return cn;
31987     },
31988     
31989     /**
31990     * register a Masonry Brick
31991     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31992     */
31993     
31994     register : function(brick)
31995     {
31996         this.bricks.push(brick);
31997         brick.masonryId = this.id;
31998     },
31999     
32000     /**
32001     * clear all the Masonry Brick
32002     */
32003     clearAll : function()
32004     {
32005         this.bricks = [];
32006         //this.getChildContainer().dom.innerHTML = "";
32007         this.el.dom.innerHTML = '';
32008     },
32009     
32010     getSelected : function()
32011     {
32012         if (!this.selectedBrick) {
32013             return false;
32014         }
32015         
32016         return this.selectedBrick;
32017     }
32018 });
32019
32020 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32021     
32022     groups: {},
32023      /**
32024     * register a Masonry Layout
32025     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32026     */
32027     
32028     register : function(layout)
32029     {
32030         this.groups[layout.id] = layout;
32031     },
32032     /**
32033     * fetch a  Masonry Layout based on the masonry layout ID
32034     * @param {string} the masonry layout to add
32035     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32036     */
32037     
32038     get: function(layout_id) {
32039         if (typeof(this.groups[layout_id]) == 'undefined') {
32040             return false;
32041         }
32042         return this.groups[layout_id] ;
32043     }
32044     
32045     
32046     
32047 });
32048
32049  
32050
32051  /**
32052  *
32053  * This is based on 
32054  * http://masonry.desandro.com
32055  *
32056  * The idea is to render all the bricks based on vertical width...
32057  *
32058  * The original code extends 'outlayer' - we might need to use that....
32059  * 
32060  */
32061
32062
32063 /**
32064  * @class Roo.bootstrap.LayoutMasonryAuto
32065  * @extends Roo.bootstrap.Component
32066  * Bootstrap Layout Masonry class
32067  * 
32068  * @constructor
32069  * Create a new Element
32070  * @param {Object} config The config object
32071  */
32072
32073 Roo.bootstrap.LayoutMasonryAuto = function(config){
32074     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32075 };
32076
32077 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32078     
32079       /**
32080      * @cfg {Boolean} isFitWidth  - resize the width..
32081      */   
32082     isFitWidth : false,  // options..
32083     /**
32084      * @cfg {Boolean} isOriginLeft = left align?
32085      */   
32086     isOriginLeft : true,
32087     /**
32088      * @cfg {Boolean} isOriginTop = top align?
32089      */   
32090     isOriginTop : false,
32091     /**
32092      * @cfg {Boolean} isLayoutInstant = no animation?
32093      */   
32094     isLayoutInstant : false, // needed?
32095     /**
32096      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32097      */   
32098     isResizingContainer : true,
32099     /**
32100      * @cfg {Number} columnWidth  width of the columns 
32101      */   
32102     
32103     columnWidth : 0,
32104     
32105     /**
32106      * @cfg {Number} maxCols maximum number of columns
32107      */   
32108     
32109     maxCols: 0,
32110     /**
32111      * @cfg {Number} padHeight padding below box..
32112      */   
32113     
32114     padHeight : 10, 
32115     
32116     /**
32117      * @cfg {Boolean} isAutoInitial defalut true
32118      */   
32119     
32120     isAutoInitial : true, 
32121     
32122     // private?
32123     gutter : 0,
32124     
32125     containerWidth: 0,
32126     initialColumnWidth : 0,
32127     currentSize : null,
32128     
32129     colYs : null, // array.
32130     maxY : 0,
32131     padWidth: 10,
32132     
32133     
32134     tag: 'div',
32135     cls: '',
32136     bricks: null, //CompositeElement
32137     cols : 0, // array?
32138     // element : null, // wrapped now this.el
32139     _isLayoutInited : null, 
32140     
32141     
32142     getAutoCreate : function(){
32143         
32144         var cfg = {
32145             tag: this.tag,
32146             cls: 'blog-masonary-wrapper ' + this.cls,
32147             cn : {
32148                 cls : 'mas-boxes masonary'
32149             }
32150         };
32151         
32152         return cfg;
32153     },
32154     
32155     getChildContainer: function( )
32156     {
32157         if (this.boxesEl) {
32158             return this.boxesEl;
32159         }
32160         
32161         this.boxesEl = this.el.select('.mas-boxes').first();
32162         
32163         return this.boxesEl;
32164     },
32165     
32166     
32167     initEvents : function()
32168     {
32169         var _this = this;
32170         
32171         if(this.isAutoInitial){
32172             Roo.log('hook children rendered');
32173             this.on('childrenrendered', function() {
32174                 Roo.log('children rendered');
32175                 _this.initial();
32176             } ,this);
32177         }
32178         
32179     },
32180     
32181     initial : function()
32182     {
32183         this.reloadItems();
32184
32185         this.currentSize = this.el.getBox(true);
32186
32187         /// was window resize... - let's see if this works..
32188         Roo.EventManager.onWindowResize(this.resize, this); 
32189
32190         if(!this.isAutoInitial){
32191             this.layout();
32192             return;
32193         }
32194         
32195         this.layout.defer(500,this);
32196     },
32197     
32198     reloadItems: function()
32199     {
32200         this.bricks = this.el.select('.masonry-brick', true);
32201         
32202         this.bricks.each(function(b) {
32203             //Roo.log(b.getSize());
32204             if (!b.attr('originalwidth')) {
32205                 b.attr('originalwidth',  b.getSize().width);
32206             }
32207             
32208         });
32209         
32210         Roo.log(this.bricks.elements.length);
32211     },
32212     
32213     resize : function()
32214     {
32215         Roo.log('resize');
32216         var cs = this.el.getBox(true);
32217         
32218         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32219             Roo.log("no change in with or X");
32220             return;
32221         }
32222         this.currentSize = cs;
32223         this.layout();
32224     },
32225     
32226     layout : function()
32227     {
32228          Roo.log('layout');
32229         this._resetLayout();
32230         //this._manageStamps();
32231       
32232         // don't animate first layout
32233         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32234         this.layoutItems( isInstant );
32235       
32236         // flag for initalized
32237         this._isLayoutInited = true;
32238     },
32239     
32240     layoutItems : function( isInstant )
32241     {
32242         //var items = this._getItemsForLayout( this.items );
32243         // original code supports filtering layout items.. we just ignore it..
32244         
32245         this._layoutItems( this.bricks , isInstant );
32246       
32247         this._postLayout();
32248     },
32249     _layoutItems : function ( items , isInstant)
32250     {
32251        //this.fireEvent( 'layout', this, items );
32252     
32253
32254         if ( !items || !items.elements.length ) {
32255           // no items, emit event with empty array
32256             return;
32257         }
32258
32259         var queue = [];
32260         items.each(function(item) {
32261             Roo.log("layout item");
32262             Roo.log(item);
32263             // get x/y object from method
32264             var position = this._getItemLayoutPosition( item );
32265             // enqueue
32266             position.item = item;
32267             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32268             queue.push( position );
32269         }, this);
32270       
32271         this._processLayoutQueue( queue );
32272     },
32273     /** Sets position of item in DOM
32274     * @param {Element} item
32275     * @param {Number} x - horizontal position
32276     * @param {Number} y - vertical position
32277     * @param {Boolean} isInstant - disables transitions
32278     */
32279     _processLayoutQueue : function( queue )
32280     {
32281         for ( var i=0, len = queue.length; i < len; i++ ) {
32282             var obj = queue[i];
32283             obj.item.position('absolute');
32284             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32285         }
32286     },
32287       
32288     
32289     /**
32290     * Any logic you want to do after each layout,
32291     * i.e. size the container
32292     */
32293     _postLayout : function()
32294     {
32295         this.resizeContainer();
32296     },
32297     
32298     resizeContainer : function()
32299     {
32300         if ( !this.isResizingContainer ) {
32301             return;
32302         }
32303         var size = this._getContainerSize();
32304         if ( size ) {
32305             this.el.setSize(size.width,size.height);
32306             this.boxesEl.setSize(size.width,size.height);
32307         }
32308     },
32309     
32310     
32311     
32312     _resetLayout : function()
32313     {
32314         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32315         this.colWidth = this.el.getWidth();
32316         //this.gutter = this.el.getWidth(); 
32317         
32318         this.measureColumns();
32319
32320         // reset column Y
32321         var i = this.cols;
32322         this.colYs = [];
32323         while (i--) {
32324             this.colYs.push( 0 );
32325         }
32326     
32327         this.maxY = 0;
32328     },
32329
32330     measureColumns : function()
32331     {
32332         this.getContainerWidth();
32333       // if columnWidth is 0, default to outerWidth of first item
32334         if ( !this.columnWidth ) {
32335             var firstItem = this.bricks.first();
32336             Roo.log(firstItem);
32337             this.columnWidth  = this.containerWidth;
32338             if (firstItem && firstItem.attr('originalwidth') ) {
32339                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32340             }
32341             // columnWidth fall back to item of first element
32342             Roo.log("set column width?");
32343                         this.initialColumnWidth = this.columnWidth  ;
32344
32345             // if first elem has no width, default to size of container
32346             
32347         }
32348         
32349         
32350         if (this.initialColumnWidth) {
32351             this.columnWidth = this.initialColumnWidth;
32352         }
32353         
32354         
32355             
32356         // column width is fixed at the top - however if container width get's smaller we should
32357         // reduce it...
32358         
32359         // this bit calcs how man columns..
32360             
32361         var columnWidth = this.columnWidth += this.gutter;
32362       
32363         // calculate columns
32364         var containerWidth = this.containerWidth + this.gutter;
32365         
32366         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32367         // fix rounding errors, typically with gutters
32368         var excess = columnWidth - containerWidth % columnWidth;
32369         
32370         
32371         // if overshoot is less than a pixel, round up, otherwise floor it
32372         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32373         cols = Math[ mathMethod ]( cols );
32374         this.cols = Math.max( cols, 1 );
32375         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32376         
32377          // padding positioning..
32378         var totalColWidth = this.cols * this.columnWidth;
32379         var padavail = this.containerWidth - totalColWidth;
32380         // so for 2 columns - we need 3 'pads'
32381         
32382         var padNeeded = (1+this.cols) * this.padWidth;
32383         
32384         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32385         
32386         this.columnWidth += padExtra
32387         //this.padWidth = Math.floor(padavail /  ( this.cols));
32388         
32389         // adjust colum width so that padding is fixed??
32390         
32391         // we have 3 columns ... total = width * 3
32392         // we have X left over... that should be used by 
32393         
32394         //if (this.expandC) {
32395             
32396         //}
32397         
32398         
32399         
32400     },
32401     
32402     getContainerWidth : function()
32403     {
32404        /* // container is parent if fit width
32405         var container = this.isFitWidth ? this.element.parentNode : this.element;
32406         // check that this.size and size are there
32407         // IE8 triggers resize on body size change, so they might not be
32408         
32409         var size = getSize( container );  //FIXME
32410         this.containerWidth = size && size.innerWidth; //FIXME
32411         */
32412          
32413         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32414         
32415     },
32416     
32417     _getItemLayoutPosition : function( item )  // what is item?
32418     {
32419         // we resize the item to our columnWidth..
32420       
32421         item.setWidth(this.columnWidth);
32422         item.autoBoxAdjust  = false;
32423         
32424         var sz = item.getSize();
32425  
32426         // how many columns does this brick span
32427         var remainder = this.containerWidth % this.columnWidth;
32428         
32429         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32430         // round if off by 1 pixel, otherwise use ceil
32431         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32432         colSpan = Math.min( colSpan, this.cols );
32433         
32434         // normally this should be '1' as we dont' currently allow multi width columns..
32435         
32436         var colGroup = this._getColGroup( colSpan );
32437         // get the minimum Y value from the columns
32438         var minimumY = Math.min.apply( Math, colGroup );
32439         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32440         
32441         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32442          
32443         // position the brick
32444         var position = {
32445             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32446             y: this.currentSize.y + minimumY + this.padHeight
32447         };
32448         
32449         Roo.log(position);
32450         // apply setHeight to necessary columns
32451         var setHeight = minimumY + sz.height + this.padHeight;
32452         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32453         
32454         var setSpan = this.cols + 1 - colGroup.length;
32455         for ( var i = 0; i < setSpan; i++ ) {
32456           this.colYs[ shortColIndex + i ] = setHeight ;
32457         }
32458       
32459         return position;
32460     },
32461     
32462     /**
32463      * @param {Number} colSpan - number of columns the element spans
32464      * @returns {Array} colGroup
32465      */
32466     _getColGroup : function( colSpan )
32467     {
32468         if ( colSpan < 2 ) {
32469           // if brick spans only one column, use all the column Ys
32470           return this.colYs;
32471         }
32472       
32473         var colGroup = [];
32474         // how many different places could this brick fit horizontally
32475         var groupCount = this.cols + 1 - colSpan;
32476         // for each group potential horizontal position
32477         for ( var i = 0; i < groupCount; i++ ) {
32478           // make an array of colY values for that one group
32479           var groupColYs = this.colYs.slice( i, i + colSpan );
32480           // and get the max value of the array
32481           colGroup[i] = Math.max.apply( Math, groupColYs );
32482         }
32483         return colGroup;
32484     },
32485     /*
32486     _manageStamp : function( stamp )
32487     {
32488         var stampSize =  stamp.getSize();
32489         var offset = stamp.getBox();
32490         // get the columns that this stamp affects
32491         var firstX = this.isOriginLeft ? offset.x : offset.right;
32492         var lastX = firstX + stampSize.width;
32493         var firstCol = Math.floor( firstX / this.columnWidth );
32494         firstCol = Math.max( 0, firstCol );
32495         
32496         var lastCol = Math.floor( lastX / this.columnWidth );
32497         // lastCol should not go over if multiple of columnWidth #425
32498         lastCol -= lastX % this.columnWidth ? 0 : 1;
32499         lastCol = Math.min( this.cols - 1, lastCol );
32500         
32501         // set colYs to bottom of the stamp
32502         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32503             stampSize.height;
32504             
32505         for ( var i = firstCol; i <= lastCol; i++ ) {
32506           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32507         }
32508     },
32509     */
32510     
32511     _getContainerSize : function()
32512     {
32513         this.maxY = Math.max.apply( Math, this.colYs );
32514         var size = {
32515             height: this.maxY
32516         };
32517       
32518         if ( this.isFitWidth ) {
32519             size.width = this._getContainerFitWidth();
32520         }
32521       
32522         return size;
32523     },
32524     
32525     _getContainerFitWidth : function()
32526     {
32527         var unusedCols = 0;
32528         // count unused columns
32529         var i = this.cols;
32530         while ( --i ) {
32531           if ( this.colYs[i] !== 0 ) {
32532             break;
32533           }
32534           unusedCols++;
32535         }
32536         // fit container to columns that have been used
32537         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32538     },
32539     
32540     needsResizeLayout : function()
32541     {
32542         var previousWidth = this.containerWidth;
32543         this.getContainerWidth();
32544         return previousWidth !== this.containerWidth;
32545     }
32546  
32547 });
32548
32549  
32550
32551  /*
32552  * - LGPL
32553  *
32554  * element
32555  * 
32556  */
32557
32558 /**
32559  * @class Roo.bootstrap.MasonryBrick
32560  * @extends Roo.bootstrap.Component
32561  * Bootstrap MasonryBrick class
32562  * 
32563  * @constructor
32564  * Create a new MasonryBrick
32565  * @param {Object} config The config object
32566  */
32567
32568 Roo.bootstrap.MasonryBrick = function(config){
32569     
32570     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32571     
32572     Roo.bootstrap.MasonryBrick.register(this);
32573     
32574     this.addEvents({
32575         // raw events
32576         /**
32577          * @event click
32578          * When a MasonryBrick is clcik
32579          * @param {Roo.bootstrap.MasonryBrick} this
32580          * @param {Roo.EventObject} e
32581          */
32582         "click" : true
32583     });
32584 };
32585
32586 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32587     
32588     /**
32589      * @cfg {String} title
32590      */   
32591     title : '',
32592     /**
32593      * @cfg {String} html
32594      */   
32595     html : '',
32596     /**
32597      * @cfg {String} bgimage
32598      */   
32599     bgimage : '',
32600     /**
32601      * @cfg {String} videourl
32602      */   
32603     videourl : '',
32604     /**
32605      * @cfg {String} cls
32606      */   
32607     cls : '',
32608     /**
32609      * @cfg {String} href
32610      */   
32611     href : '',
32612     /**
32613      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32614      */   
32615     size : 'xs',
32616     
32617     /**
32618      * @cfg {String} placetitle (center|bottom)
32619      */   
32620     placetitle : '',
32621     
32622     /**
32623      * @cfg {Boolean} isFitContainer defalut true
32624      */   
32625     isFitContainer : true, 
32626     
32627     /**
32628      * @cfg {Boolean} preventDefault defalut false
32629      */   
32630     preventDefault : false, 
32631     
32632     /**
32633      * @cfg {Boolean} inverse defalut false
32634      */   
32635     maskInverse : false, 
32636     
32637     getAutoCreate : function()
32638     {
32639         if(!this.isFitContainer){
32640             return this.getSplitAutoCreate();
32641         }
32642         
32643         var cls = 'masonry-brick masonry-brick-full';
32644         
32645         if(this.href.length){
32646             cls += ' masonry-brick-link';
32647         }
32648         
32649         if(this.bgimage.length){
32650             cls += ' masonry-brick-image';
32651         }
32652         
32653         if(this.maskInverse){
32654             cls += ' mask-inverse';
32655         }
32656         
32657         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32658             cls += ' enable-mask';
32659         }
32660         
32661         if(this.size){
32662             cls += ' masonry-' + this.size + '-brick';
32663         }
32664         
32665         if(this.placetitle.length){
32666             
32667             switch (this.placetitle) {
32668                 case 'center' :
32669                     cls += ' masonry-center-title';
32670                     break;
32671                 case 'bottom' :
32672                     cls += ' masonry-bottom-title';
32673                     break;
32674                 default:
32675                     break;
32676             }
32677             
32678         } else {
32679             if(!this.html.length && !this.bgimage.length){
32680                 cls += ' masonry-center-title';
32681             }
32682
32683             if(!this.html.length && this.bgimage.length){
32684                 cls += ' masonry-bottom-title';
32685             }
32686         }
32687         
32688         if(this.cls){
32689             cls += ' ' + this.cls;
32690         }
32691         
32692         var cfg = {
32693             tag: (this.href.length) ? 'a' : 'div',
32694             cls: cls,
32695             cn: [
32696                 {
32697                     tag: 'div',
32698                     cls: 'masonry-brick-mask'
32699                 },
32700                 {
32701                     tag: 'div',
32702                     cls: 'masonry-brick-paragraph',
32703                     cn: []
32704                 }
32705             ]
32706         };
32707         
32708         if(this.href.length){
32709             cfg.href = this.href;
32710         }
32711         
32712         var cn = cfg.cn[1].cn;
32713         
32714         if(this.title.length){
32715             cn.push({
32716                 tag: 'h4',
32717                 cls: 'masonry-brick-title',
32718                 html: this.title
32719             });
32720         }
32721         
32722         if(this.html.length){
32723             cn.push({
32724                 tag: 'p',
32725                 cls: 'masonry-brick-text',
32726                 html: this.html
32727             });
32728         }
32729         
32730         if (!this.title.length && !this.html.length) {
32731             cfg.cn[1].cls += ' hide';
32732         }
32733         
32734         if(this.bgimage.length){
32735             cfg.cn.push({
32736                 tag: 'img',
32737                 cls: 'masonry-brick-image-view',
32738                 src: this.bgimage
32739             });
32740         }
32741         
32742         if(this.videourl.length){
32743             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32744             // youtube support only?
32745             cfg.cn.push({
32746                 tag: 'iframe',
32747                 cls: 'masonry-brick-image-view',
32748                 src: vurl,
32749                 frameborder : 0,
32750                 allowfullscreen : true
32751             });
32752         }
32753         
32754         return cfg;
32755         
32756     },
32757     
32758     getSplitAutoCreate : function()
32759     {
32760         var cls = 'masonry-brick masonry-brick-split';
32761         
32762         if(this.href.length){
32763             cls += ' masonry-brick-link';
32764         }
32765         
32766         if(this.bgimage.length){
32767             cls += ' masonry-brick-image';
32768         }
32769         
32770         if(this.size){
32771             cls += ' masonry-' + this.size + '-brick';
32772         }
32773         
32774         switch (this.placetitle) {
32775             case 'center' :
32776                 cls += ' masonry-center-title';
32777                 break;
32778             case 'bottom' :
32779                 cls += ' masonry-bottom-title';
32780                 break;
32781             default:
32782                 if(!this.bgimage.length){
32783                     cls += ' masonry-center-title';
32784                 }
32785
32786                 if(this.bgimage.length){
32787                     cls += ' masonry-bottom-title';
32788                 }
32789                 break;
32790         }
32791         
32792         if(this.cls){
32793             cls += ' ' + this.cls;
32794         }
32795         
32796         var cfg = {
32797             tag: (this.href.length) ? 'a' : 'div',
32798             cls: cls,
32799             cn: [
32800                 {
32801                     tag: 'div',
32802                     cls: 'masonry-brick-split-head',
32803                     cn: [
32804                         {
32805                             tag: 'div',
32806                             cls: 'masonry-brick-paragraph',
32807                             cn: []
32808                         }
32809                     ]
32810                 },
32811                 {
32812                     tag: 'div',
32813                     cls: 'masonry-brick-split-body',
32814                     cn: []
32815                 }
32816             ]
32817         };
32818         
32819         if(this.href.length){
32820             cfg.href = this.href;
32821         }
32822         
32823         if(this.title.length){
32824             cfg.cn[0].cn[0].cn.push({
32825                 tag: 'h4',
32826                 cls: 'masonry-brick-title',
32827                 html: this.title
32828             });
32829         }
32830         
32831         if(this.html.length){
32832             cfg.cn[1].cn.push({
32833                 tag: 'p',
32834                 cls: 'masonry-brick-text',
32835                 html: this.html
32836             });
32837         }
32838
32839         if(this.bgimage.length){
32840             cfg.cn[0].cn.push({
32841                 tag: 'img',
32842                 cls: 'masonry-brick-image-view',
32843                 src: this.bgimage
32844             });
32845         }
32846         
32847         if(this.videourl.length){
32848             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32849             // youtube support only?
32850             cfg.cn[0].cn.cn.push({
32851                 tag: 'iframe',
32852                 cls: 'masonry-brick-image-view',
32853                 src: vurl,
32854                 frameborder : 0,
32855                 allowfullscreen : true
32856             });
32857         }
32858         
32859         return cfg;
32860     },
32861     
32862     initEvents: function() 
32863     {
32864         switch (this.size) {
32865             case 'xs' :
32866                 this.x = 1;
32867                 this.y = 1;
32868                 break;
32869             case 'sm' :
32870                 this.x = 2;
32871                 this.y = 2;
32872                 break;
32873             case 'md' :
32874             case 'md-left' :
32875             case 'md-right' :
32876                 this.x = 3;
32877                 this.y = 3;
32878                 break;
32879             case 'tall' :
32880                 this.x = 2;
32881                 this.y = 3;
32882                 break;
32883             case 'wide' :
32884                 this.x = 3;
32885                 this.y = 2;
32886                 break;
32887             case 'wide-thin' :
32888                 this.x = 3;
32889                 this.y = 1;
32890                 break;
32891                         
32892             default :
32893                 break;
32894         }
32895         
32896         if(Roo.isTouch){
32897             this.el.on('touchstart', this.onTouchStart, this);
32898             this.el.on('touchmove', this.onTouchMove, this);
32899             this.el.on('touchend', this.onTouchEnd, this);
32900             this.el.on('contextmenu', this.onContextMenu, this);
32901         } else {
32902             this.el.on('mouseenter'  ,this.enter, this);
32903             this.el.on('mouseleave', this.leave, this);
32904             this.el.on('click', this.onClick, this);
32905         }
32906         
32907         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32908             this.parent().bricks.push(this);   
32909         }
32910         
32911     },
32912     
32913     onClick: function(e, el)
32914     {
32915         var time = this.endTimer - this.startTimer;
32916         // Roo.log(e.preventDefault());
32917         if(Roo.isTouch){
32918             if(time > 1000){
32919                 e.preventDefault();
32920                 return;
32921             }
32922         }
32923         
32924         if(!this.preventDefault){
32925             return;
32926         }
32927         
32928         e.preventDefault();
32929         
32930         if (this.activeClass != '') {
32931             this.selectBrick();
32932         }
32933         
32934         this.fireEvent('click', this, e);
32935     },
32936     
32937     enter: function(e, el)
32938     {
32939         e.preventDefault();
32940         
32941         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32942             return;
32943         }
32944         
32945         if(this.bgimage.length && this.html.length){
32946             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32947         }
32948     },
32949     
32950     leave: function(e, el)
32951     {
32952         e.preventDefault();
32953         
32954         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32955             return;
32956         }
32957         
32958         if(this.bgimage.length && this.html.length){
32959             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32960         }
32961     },
32962     
32963     onTouchStart: function(e, el)
32964     {
32965 //        e.preventDefault();
32966         
32967         this.touchmoved = false;
32968         
32969         if(!this.isFitContainer){
32970             return;
32971         }
32972         
32973         if(!this.bgimage.length || !this.html.length){
32974             return;
32975         }
32976         
32977         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32978         
32979         this.timer = new Date().getTime();
32980         
32981     },
32982     
32983     onTouchMove: function(e, el)
32984     {
32985         this.touchmoved = true;
32986     },
32987     
32988     onContextMenu : function(e,el)
32989     {
32990         e.preventDefault();
32991         e.stopPropagation();
32992         return false;
32993     },
32994     
32995     onTouchEnd: function(e, el)
32996     {
32997 //        e.preventDefault();
32998         
32999         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33000         
33001             this.leave(e,el);
33002             
33003             return;
33004         }
33005         
33006         if(!this.bgimage.length || !this.html.length){
33007             
33008             if(this.href.length){
33009                 window.location.href = this.href;
33010             }
33011             
33012             return;
33013         }
33014         
33015         if(!this.isFitContainer){
33016             return;
33017         }
33018         
33019         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33020         
33021         window.location.href = this.href;
33022     },
33023     
33024     //selection on single brick only
33025     selectBrick : function() {
33026         
33027         if (!this.parentId) {
33028             return;
33029         }
33030         
33031         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33032         var index = m.selectedBrick.indexOf(this.id);
33033         
33034         if ( index > -1) {
33035             m.selectedBrick.splice(index,1);
33036             this.el.removeClass(this.activeClass);
33037             return;
33038         }
33039         
33040         for(var i = 0; i < m.selectedBrick.length; i++) {
33041             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33042             b.el.removeClass(b.activeClass);
33043         }
33044         
33045         m.selectedBrick = [];
33046         
33047         m.selectedBrick.push(this.id);
33048         this.el.addClass(this.activeClass);
33049         return;
33050     },
33051     
33052     isSelected : function(){
33053         return this.el.hasClass(this.activeClass);
33054         
33055     }
33056 });
33057
33058 Roo.apply(Roo.bootstrap.MasonryBrick, {
33059     
33060     //groups: {},
33061     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33062      /**
33063     * register a Masonry Brick
33064     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33065     */
33066     
33067     register : function(brick)
33068     {
33069         //this.groups[brick.id] = brick;
33070         this.groups.add(brick.id, brick);
33071     },
33072     /**
33073     * fetch a  masonry brick based on the masonry brick ID
33074     * @param {string} the masonry brick to add
33075     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33076     */
33077     
33078     get: function(brick_id) 
33079     {
33080         // if (typeof(this.groups[brick_id]) == 'undefined') {
33081         //     return false;
33082         // }
33083         // return this.groups[brick_id] ;
33084         
33085         if(this.groups.key(brick_id)) {
33086             return this.groups.key(brick_id);
33087         }
33088         
33089         return false;
33090     }
33091     
33092     
33093     
33094 });
33095
33096  /*
33097  * - LGPL
33098  *
33099  * element
33100  * 
33101  */
33102
33103 /**
33104  * @class Roo.bootstrap.Brick
33105  * @extends Roo.bootstrap.Component
33106  * Bootstrap Brick class
33107  * 
33108  * @constructor
33109  * Create a new Brick
33110  * @param {Object} config The config object
33111  */
33112
33113 Roo.bootstrap.Brick = function(config){
33114     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33115     
33116     this.addEvents({
33117         // raw events
33118         /**
33119          * @event click
33120          * When a Brick is click
33121          * @param {Roo.bootstrap.Brick} this
33122          * @param {Roo.EventObject} e
33123          */
33124         "click" : true
33125     });
33126 };
33127
33128 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33129     
33130     /**
33131      * @cfg {String} title
33132      */   
33133     title : '',
33134     /**
33135      * @cfg {String} html
33136      */   
33137     html : '',
33138     /**
33139      * @cfg {String} bgimage
33140      */   
33141     bgimage : '',
33142     /**
33143      * @cfg {String} cls
33144      */   
33145     cls : '',
33146     /**
33147      * @cfg {String} href
33148      */   
33149     href : '',
33150     /**
33151      * @cfg {String} video
33152      */   
33153     video : '',
33154     /**
33155      * @cfg {Boolean} square
33156      */   
33157     square : true,
33158     
33159     getAutoCreate : function()
33160     {
33161         var cls = 'roo-brick';
33162         
33163         if(this.href.length){
33164             cls += ' roo-brick-link';
33165         }
33166         
33167         if(this.bgimage.length){
33168             cls += ' roo-brick-image';
33169         }
33170         
33171         if(!this.html.length && !this.bgimage.length){
33172             cls += ' roo-brick-center-title';
33173         }
33174         
33175         if(!this.html.length && this.bgimage.length){
33176             cls += ' roo-brick-bottom-title';
33177         }
33178         
33179         if(this.cls){
33180             cls += ' ' + this.cls;
33181         }
33182         
33183         var cfg = {
33184             tag: (this.href.length) ? 'a' : 'div',
33185             cls: cls,
33186             cn: [
33187                 {
33188                     tag: 'div',
33189                     cls: 'roo-brick-paragraph',
33190                     cn: []
33191                 }
33192             ]
33193         };
33194         
33195         if(this.href.length){
33196             cfg.href = this.href;
33197         }
33198         
33199         var cn = cfg.cn[0].cn;
33200         
33201         if(this.title.length){
33202             cn.push({
33203                 tag: 'h4',
33204                 cls: 'roo-brick-title',
33205                 html: this.title
33206             });
33207         }
33208         
33209         if(this.html.length){
33210             cn.push({
33211                 tag: 'p',
33212                 cls: 'roo-brick-text',
33213                 html: this.html
33214             });
33215         } else {
33216             cn.cls += ' hide';
33217         }
33218         
33219         if(this.bgimage.length){
33220             cfg.cn.push({
33221                 tag: 'img',
33222                 cls: 'roo-brick-image-view',
33223                 src: this.bgimage
33224             });
33225         }
33226         
33227         return cfg;
33228     },
33229     
33230     initEvents: function() 
33231     {
33232         if(this.title.length || this.html.length){
33233             this.el.on('mouseenter'  ,this.enter, this);
33234             this.el.on('mouseleave', this.leave, this);
33235         }
33236         
33237         Roo.EventManager.onWindowResize(this.resize, this); 
33238         
33239         if(this.bgimage.length){
33240             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33241             this.imageEl.on('load', this.onImageLoad, this);
33242             return;
33243         }
33244         
33245         this.resize();
33246     },
33247     
33248     onImageLoad : function()
33249     {
33250         this.resize();
33251     },
33252     
33253     resize : function()
33254     {
33255         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33256         
33257         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33258         
33259         if(this.bgimage.length){
33260             var image = this.el.select('.roo-brick-image-view', true).first();
33261             
33262             image.setWidth(paragraph.getWidth());
33263             
33264             if(this.square){
33265                 image.setHeight(paragraph.getWidth());
33266             }
33267             
33268             this.el.setHeight(image.getHeight());
33269             paragraph.setHeight(image.getHeight());
33270             
33271         }
33272         
33273     },
33274     
33275     enter: function(e, el)
33276     {
33277         e.preventDefault();
33278         
33279         if(this.bgimage.length){
33280             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33281             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33282         }
33283     },
33284     
33285     leave: function(e, el)
33286     {
33287         e.preventDefault();
33288         
33289         if(this.bgimage.length){
33290             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33291             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33292         }
33293     }
33294     
33295 });
33296
33297  
33298
33299  /*
33300  * - LGPL
33301  *
33302  * Number field 
33303  */
33304
33305 /**
33306  * @class Roo.bootstrap.NumberField
33307  * @extends Roo.bootstrap.Input
33308  * Bootstrap NumberField class
33309  * 
33310  * 
33311  * 
33312  * 
33313  * @constructor
33314  * Create a new NumberField
33315  * @param {Object} config The config object
33316  */
33317
33318 Roo.bootstrap.NumberField = function(config){
33319     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33320 };
33321
33322 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33323     
33324     /**
33325      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33326      */
33327     allowDecimals : true,
33328     /**
33329      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33330      */
33331     decimalSeparator : ".",
33332     /**
33333      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33334      */
33335     decimalPrecision : 2,
33336     /**
33337      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33338      */
33339     allowNegative : true,
33340     
33341     /**
33342      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33343      */
33344     allowZero: true,
33345     /**
33346      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33347      */
33348     minValue : Number.NEGATIVE_INFINITY,
33349     /**
33350      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33351      */
33352     maxValue : Number.MAX_VALUE,
33353     /**
33354      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33355      */
33356     minText : "The minimum value for this field is {0}",
33357     /**
33358      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33359      */
33360     maxText : "The maximum value for this field is {0}",
33361     /**
33362      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33363      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33364      */
33365     nanText : "{0} is not a valid number",
33366     /**
33367      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33368      */
33369     thousandsDelimiter : false,
33370     /**
33371      * @cfg {String} valueAlign alignment of value
33372      */
33373     valueAlign : "left",
33374
33375     getAutoCreate : function()
33376     {
33377         var hiddenInput = {
33378             tag: 'input',
33379             type: 'hidden',
33380             id: Roo.id(),
33381             cls: 'hidden-number-input'
33382         };
33383         
33384         if (this.name) {
33385             hiddenInput.name = this.name;
33386         }
33387         
33388         this.name = '';
33389         
33390         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33391         
33392         this.name = hiddenInput.name;
33393         
33394         if(cfg.cn.length > 0) {
33395             cfg.cn.push(hiddenInput);
33396         }
33397         
33398         return cfg;
33399     },
33400
33401     // private
33402     initEvents : function()
33403     {   
33404         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33405         
33406         var allowed = "0123456789";
33407         
33408         if(this.allowDecimals){
33409             allowed += this.decimalSeparator;
33410         }
33411         
33412         if(this.allowNegative){
33413             allowed += "-";
33414         }
33415         
33416         if(this.thousandsDelimiter) {
33417             allowed += ",";
33418         }
33419         
33420         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33421         
33422         var keyPress = function(e){
33423             
33424             var k = e.getKey();
33425             
33426             var c = e.getCharCode();
33427             
33428             if(
33429                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33430                     allowed.indexOf(String.fromCharCode(c)) === -1
33431             ){
33432                 e.stopEvent();
33433                 return;
33434             }
33435             
33436             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33437                 return;
33438             }
33439             
33440             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33441                 e.stopEvent();
33442             }
33443         };
33444         
33445         this.el.on("keypress", keyPress, this);
33446     },
33447     
33448     validateValue : function(value)
33449     {
33450         
33451         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33452             return false;
33453         }
33454         
33455         var num = this.parseValue(value);
33456         
33457         if(isNaN(num)){
33458             this.markInvalid(String.format(this.nanText, value));
33459             return false;
33460         }
33461         
33462         if(num < this.minValue){
33463             this.markInvalid(String.format(this.minText, this.minValue));
33464             return false;
33465         }
33466         
33467         if(num > this.maxValue){
33468             this.markInvalid(String.format(this.maxText, this.maxValue));
33469             return false;
33470         }
33471         
33472         return true;
33473     },
33474
33475     getValue : function()
33476     {
33477         var v = this.hiddenEl().getValue();
33478         
33479         return this.fixPrecision(this.parseValue(v));
33480     },
33481
33482     parseValue : function(value)
33483     {
33484         if(this.thousandsDelimiter) {
33485             value += "";
33486             r = new RegExp(",", "g");
33487             value = value.replace(r, "");
33488         }
33489         
33490         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33491         return isNaN(value) ? '' : value;
33492     },
33493
33494     fixPrecision : function(value)
33495     {
33496         if(this.thousandsDelimiter) {
33497             value += "";
33498             r = new RegExp(",", "g");
33499             value = value.replace(r, "");
33500         }
33501         
33502         var nan = isNaN(value);
33503         
33504         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33505             return nan ? '' : value;
33506         }
33507         return parseFloat(value).toFixed(this.decimalPrecision);
33508     },
33509
33510     setValue : function(v)
33511     {
33512         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33513         
33514         this.value = v;
33515         
33516         if(this.rendered){
33517             
33518             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33519             
33520             this.inputEl().dom.value = (v == '') ? '' :
33521                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33522             
33523             if(!this.allowZero && v === '0') {
33524                 this.hiddenEl().dom.value = '';
33525                 this.inputEl().dom.value = '';
33526             }
33527             
33528             this.validate();
33529         }
33530     },
33531
33532     decimalPrecisionFcn : function(v)
33533     {
33534         return Math.floor(v);
33535     },
33536
33537     beforeBlur : function()
33538     {
33539         var v = this.parseValue(this.getRawValue());
33540         
33541         if(v || v === 0 || v === ''){
33542             this.setValue(v);
33543         }
33544     },
33545     
33546     hiddenEl : function()
33547     {
33548         return this.el.select('input.hidden-number-input',true).first();
33549     }
33550     
33551 });
33552
33553  
33554
33555 /*
33556 * Licence: LGPL
33557 */
33558
33559 /**
33560  * @class Roo.bootstrap.DocumentSlider
33561  * @extends Roo.bootstrap.Component
33562  * Bootstrap DocumentSlider class
33563  * 
33564  * @constructor
33565  * Create a new DocumentViewer
33566  * @param {Object} config The config object
33567  */
33568
33569 Roo.bootstrap.DocumentSlider = function(config){
33570     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33571     
33572     this.files = [];
33573     
33574     this.addEvents({
33575         /**
33576          * @event initial
33577          * Fire after initEvent
33578          * @param {Roo.bootstrap.DocumentSlider} this
33579          */
33580         "initial" : true,
33581         /**
33582          * @event update
33583          * Fire after update
33584          * @param {Roo.bootstrap.DocumentSlider} this
33585          */
33586         "update" : true,
33587         /**
33588          * @event click
33589          * Fire after click
33590          * @param {Roo.bootstrap.DocumentSlider} this
33591          */
33592         "click" : true
33593     });
33594 };
33595
33596 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33597     
33598     files : false,
33599     
33600     indicator : 0,
33601     
33602     getAutoCreate : function()
33603     {
33604         var cfg = {
33605             tag : 'div',
33606             cls : 'roo-document-slider',
33607             cn : [
33608                 {
33609                     tag : 'div',
33610                     cls : 'roo-document-slider-header',
33611                     cn : [
33612                         {
33613                             tag : 'div',
33614                             cls : 'roo-document-slider-header-title'
33615                         }
33616                     ]
33617                 },
33618                 {
33619                     tag : 'div',
33620                     cls : 'roo-document-slider-body',
33621                     cn : [
33622                         {
33623                             tag : 'div',
33624                             cls : 'roo-document-slider-prev',
33625                             cn : [
33626                                 {
33627                                     tag : 'i',
33628                                     cls : 'fa fa-chevron-left'
33629                                 }
33630                             ]
33631                         },
33632                         {
33633                             tag : 'div',
33634                             cls : 'roo-document-slider-thumb',
33635                             cn : [
33636                                 {
33637                                     tag : 'img',
33638                                     cls : 'roo-document-slider-image'
33639                                 }
33640                             ]
33641                         },
33642                         {
33643                             tag : 'div',
33644                             cls : 'roo-document-slider-next',
33645                             cn : [
33646                                 {
33647                                     tag : 'i',
33648                                     cls : 'fa fa-chevron-right'
33649                                 }
33650                             ]
33651                         }
33652                     ]
33653                 }
33654             ]
33655         };
33656         
33657         return cfg;
33658     },
33659     
33660     initEvents : function()
33661     {
33662         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33663         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33664         
33665         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33666         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33667         
33668         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33669         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33670         
33671         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33672         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33673         
33674         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33675         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33676         
33677         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33678         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33679         
33680         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33681         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33682         
33683         this.thumbEl.on('click', this.onClick, this);
33684         
33685         this.prevIndicator.on('click', this.prev, this);
33686         
33687         this.nextIndicator.on('click', this.next, this);
33688         
33689     },
33690     
33691     initial : function()
33692     {
33693         if(this.files.length){
33694             this.indicator = 1;
33695             this.update()
33696         }
33697         
33698         this.fireEvent('initial', this);
33699     },
33700     
33701     update : function()
33702     {
33703         this.imageEl.attr('src', this.files[this.indicator - 1]);
33704         
33705         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33706         
33707         this.prevIndicator.show();
33708         
33709         if(this.indicator == 1){
33710             this.prevIndicator.hide();
33711         }
33712         
33713         this.nextIndicator.show();
33714         
33715         if(this.indicator == this.files.length){
33716             this.nextIndicator.hide();
33717         }
33718         
33719         this.thumbEl.scrollTo('top');
33720         
33721         this.fireEvent('update', this);
33722     },
33723     
33724     onClick : function(e)
33725     {
33726         e.preventDefault();
33727         
33728         this.fireEvent('click', this);
33729     },
33730     
33731     prev : function(e)
33732     {
33733         e.preventDefault();
33734         
33735         this.indicator = Math.max(1, this.indicator - 1);
33736         
33737         this.update();
33738     },
33739     
33740     next : function(e)
33741     {
33742         e.preventDefault();
33743         
33744         this.indicator = Math.min(this.files.length, this.indicator + 1);
33745         
33746         this.update();
33747     }
33748 });
33749 /*
33750  * - LGPL
33751  *
33752  * RadioSet
33753  *
33754  *
33755  */
33756
33757 /**
33758  * @class Roo.bootstrap.RadioSet
33759  * @extends Roo.bootstrap.Input
33760  * Bootstrap RadioSet class
33761  * @cfg {String} indicatorpos (left|right) default left
33762  * @cfg {Boolean} inline (true|false) inline the element (default true)
33763  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33764  * @constructor
33765  * Create a new RadioSet
33766  * @param {Object} config The config object
33767  */
33768
33769 Roo.bootstrap.RadioSet = function(config){
33770     
33771     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33772     
33773     this.radioes = [];
33774     
33775     Roo.bootstrap.RadioSet.register(this);
33776     
33777     this.addEvents({
33778         /**
33779         * @event check
33780         * Fires when the element is checked or unchecked.
33781         * @param {Roo.bootstrap.RadioSet} this This radio
33782         * @param {Roo.bootstrap.Radio} item The checked item
33783         */
33784        check : true,
33785        /**
33786         * @event click
33787         * Fires when the element is click.
33788         * @param {Roo.bootstrap.RadioSet} this This radio set
33789         * @param {Roo.bootstrap.Radio} item The checked item
33790         * @param {Roo.EventObject} e The event object
33791         */
33792        click : true
33793     });
33794     
33795 };
33796
33797 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33798
33799     radioes : false,
33800     
33801     inline : true,
33802     
33803     weight : '',
33804     
33805     indicatorpos : 'left',
33806     
33807     getAutoCreate : function()
33808     {
33809         var label = {
33810             tag : 'label',
33811             cls : 'roo-radio-set-label',
33812             cn : [
33813                 {
33814                     tag : 'span',
33815                     html : this.fieldLabel
33816                 }
33817             ]
33818         };
33819         
33820         if(this.indicatorpos == 'left'){
33821             label.cn.unshift({
33822                 tag : 'i',
33823                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33824                 tooltip : 'This field is required'
33825             });
33826         } else {
33827             label.cn.push({
33828                 tag : 'i',
33829                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33830                 tooltip : 'This field is required'
33831             });
33832         }
33833         
33834         var items = {
33835             tag : 'div',
33836             cls : 'roo-radio-set-items'
33837         };
33838         
33839         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33840         
33841         if (align === 'left' && this.fieldLabel.length) {
33842             
33843             items = {
33844                 cls : "roo-radio-set-right", 
33845                 cn: [
33846                     items
33847                 ]
33848             };
33849             
33850             if(this.labelWidth > 12){
33851                 label.style = "width: " + this.labelWidth + 'px';
33852             }
33853             
33854             if(this.labelWidth < 13 && this.labelmd == 0){
33855                 this.labelmd = this.labelWidth;
33856             }
33857             
33858             if(this.labellg > 0){
33859                 label.cls += ' col-lg-' + this.labellg;
33860                 items.cls += ' col-lg-' + (12 - this.labellg);
33861             }
33862             
33863             if(this.labelmd > 0){
33864                 label.cls += ' col-md-' + this.labelmd;
33865                 items.cls += ' col-md-' + (12 - this.labelmd);
33866             }
33867             
33868             if(this.labelsm > 0){
33869                 label.cls += ' col-sm-' + this.labelsm;
33870                 items.cls += ' col-sm-' + (12 - this.labelsm);
33871             }
33872             
33873             if(this.labelxs > 0){
33874                 label.cls += ' col-xs-' + this.labelxs;
33875                 items.cls += ' col-xs-' + (12 - this.labelxs);
33876             }
33877         }
33878         
33879         var cfg = {
33880             tag : 'div',
33881             cls : 'roo-radio-set',
33882             cn : [
33883                 {
33884                     tag : 'input',
33885                     cls : 'roo-radio-set-input',
33886                     type : 'hidden',
33887                     name : this.name,
33888                     value : this.value ? this.value :  ''
33889                 },
33890                 label,
33891                 items
33892             ]
33893         };
33894         
33895         if(this.weight.length){
33896             cfg.cls += ' roo-radio-' + this.weight;
33897         }
33898         
33899         if(this.inline) {
33900             cfg.cls += ' roo-radio-set-inline';
33901         }
33902         
33903         var settings=this;
33904         ['xs','sm','md','lg'].map(function(size){
33905             if (settings[size]) {
33906                 cfg.cls += ' col-' + size + '-' + settings[size];
33907             }
33908         });
33909         
33910         return cfg;
33911         
33912     },
33913
33914     initEvents : function()
33915     {
33916         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33917         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33918         
33919         if(!this.fieldLabel.length){
33920             this.labelEl.hide();
33921         }
33922         
33923         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33924         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33925         
33926         this.indicator = this.indicatorEl();
33927         
33928         if(this.indicator){
33929             this.indicator.addClass('invisible');
33930         }
33931         
33932         this.originalValue = this.getValue();
33933         
33934     },
33935     
33936     inputEl: function ()
33937     {
33938         return this.el.select('.roo-radio-set-input', true).first();
33939     },
33940     
33941     getChildContainer : function()
33942     {
33943         return this.itemsEl;
33944     },
33945     
33946     register : function(item)
33947     {
33948         this.radioes.push(item);
33949         
33950     },
33951     
33952     validate : function()
33953     {   
33954         if(this.getVisibilityEl().hasClass('hidden')){
33955             return true;
33956         }
33957         
33958         var valid = false;
33959         
33960         Roo.each(this.radioes, function(i){
33961             if(!i.checked){
33962                 return;
33963             }
33964             
33965             valid = true;
33966             return false;
33967         });
33968         
33969         if(this.allowBlank) {
33970             return true;
33971         }
33972         
33973         if(this.disabled || valid){
33974             this.markValid();
33975             return true;
33976         }
33977         
33978         this.markInvalid();
33979         return false;
33980         
33981     },
33982     
33983     markValid : function()
33984     {
33985         if(this.labelEl.isVisible(true)){
33986             this.indicatorEl().removeClass('visible');
33987             this.indicatorEl().addClass('invisible');
33988         }
33989         
33990         this.el.removeClass([this.invalidClass, this.validClass]);
33991         this.el.addClass(this.validClass);
33992         
33993         this.fireEvent('valid', this);
33994     },
33995     
33996     markInvalid : function(msg)
33997     {
33998         if(this.allowBlank || this.disabled){
33999             return;
34000         }
34001         
34002         if(this.labelEl.isVisible(true)){
34003             this.indicatorEl().removeClass('invisible');
34004             this.indicatorEl().addClass('visible');
34005         }
34006         
34007         this.el.removeClass([this.invalidClass, this.validClass]);
34008         this.el.addClass(this.invalidClass);
34009         
34010         this.fireEvent('invalid', this, msg);
34011         
34012     },
34013     
34014     setValue : function(v, suppressEvent)
34015     {   
34016         if(this.value === v){
34017             return;
34018         }
34019         
34020         this.value = v;
34021         
34022         if(this.rendered){
34023             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34024         }
34025         
34026         Roo.each(this.radioes, function(i){
34027             i.checked = false;
34028             i.el.removeClass('checked');
34029         });
34030         
34031         Roo.each(this.radioes, function(i){
34032             
34033             if(i.value === v || i.value.toString() === v.toString()){
34034                 i.checked = true;
34035                 i.el.addClass('checked');
34036                 
34037                 if(suppressEvent !== true){
34038                     this.fireEvent('check', this, i);
34039                 }
34040                 
34041                 return false;
34042             }
34043             
34044         }, this);
34045         
34046         this.validate();
34047     },
34048     
34049     clearInvalid : function(){
34050         
34051         if(!this.el || this.preventMark){
34052             return;
34053         }
34054         
34055         this.el.removeClass([this.invalidClass]);
34056         
34057         this.fireEvent('valid', this);
34058     }
34059     
34060 });
34061
34062 Roo.apply(Roo.bootstrap.RadioSet, {
34063     
34064     groups: {},
34065     
34066     register : function(set)
34067     {
34068         this.groups[set.name] = set;
34069     },
34070     
34071     get: function(name) 
34072     {
34073         if (typeof(this.groups[name]) == 'undefined') {
34074             return false;
34075         }
34076         
34077         return this.groups[name] ;
34078     }
34079     
34080 });
34081 /*
34082  * Based on:
34083  * Ext JS Library 1.1.1
34084  * Copyright(c) 2006-2007, Ext JS, LLC.
34085  *
34086  * Originally Released Under LGPL - original licence link has changed is not relivant.
34087  *
34088  * Fork - LGPL
34089  * <script type="text/javascript">
34090  */
34091
34092
34093 /**
34094  * @class Roo.bootstrap.SplitBar
34095  * @extends Roo.util.Observable
34096  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34097  * <br><br>
34098  * Usage:
34099  * <pre><code>
34100 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34101                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34102 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34103 split.minSize = 100;
34104 split.maxSize = 600;
34105 split.animate = true;
34106 split.on('moved', splitterMoved);
34107 </code></pre>
34108  * @constructor
34109  * Create a new SplitBar
34110  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34111  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34112  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34113  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34114                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34115                         position of the SplitBar).
34116  */
34117 Roo.bootstrap.SplitBar = function(cfg){
34118     
34119     /** @private */
34120     
34121     //{
34122     //  dragElement : elm
34123     //  resizingElement: el,
34124         // optional..
34125     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34126     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34127         // existingProxy ???
34128     //}
34129     
34130     this.el = Roo.get(cfg.dragElement, true);
34131     this.el.dom.unselectable = "on";
34132     /** @private */
34133     this.resizingEl = Roo.get(cfg.resizingElement, true);
34134
34135     /**
34136      * @private
34137      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34138      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34139      * @type Number
34140      */
34141     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34142     
34143     /**
34144      * The minimum size of the resizing element. (Defaults to 0)
34145      * @type Number
34146      */
34147     this.minSize = 0;
34148     
34149     /**
34150      * The maximum size of the resizing element. (Defaults to 2000)
34151      * @type Number
34152      */
34153     this.maxSize = 2000;
34154     
34155     /**
34156      * Whether to animate the transition to the new size
34157      * @type Boolean
34158      */
34159     this.animate = false;
34160     
34161     /**
34162      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34163      * @type Boolean
34164      */
34165     this.useShim = false;
34166     
34167     /** @private */
34168     this.shim = null;
34169     
34170     if(!cfg.existingProxy){
34171         /** @private */
34172         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34173     }else{
34174         this.proxy = Roo.get(cfg.existingProxy).dom;
34175     }
34176     /** @private */
34177     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34178     
34179     /** @private */
34180     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34181     
34182     /** @private */
34183     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34184     
34185     /** @private */
34186     this.dragSpecs = {};
34187     
34188     /**
34189      * @private The adapter to use to positon and resize elements
34190      */
34191     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34192     this.adapter.init(this);
34193     
34194     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34195         /** @private */
34196         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34197         this.el.addClass("roo-splitbar-h");
34198     }else{
34199         /** @private */
34200         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34201         this.el.addClass("roo-splitbar-v");
34202     }
34203     
34204     this.addEvents({
34205         /**
34206          * @event resize
34207          * Fires when the splitter is moved (alias for {@link #event-moved})
34208          * @param {Roo.bootstrap.SplitBar} this
34209          * @param {Number} newSize the new width or height
34210          */
34211         "resize" : true,
34212         /**
34213          * @event moved
34214          * Fires when the splitter is moved
34215          * @param {Roo.bootstrap.SplitBar} this
34216          * @param {Number} newSize the new width or height
34217          */
34218         "moved" : true,
34219         /**
34220          * @event beforeresize
34221          * Fires before the splitter is dragged
34222          * @param {Roo.bootstrap.SplitBar} this
34223          */
34224         "beforeresize" : true,
34225
34226         "beforeapply" : true
34227     });
34228
34229     Roo.util.Observable.call(this);
34230 };
34231
34232 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34233     onStartProxyDrag : function(x, y){
34234         this.fireEvent("beforeresize", this);
34235         if(!this.overlay){
34236             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34237             o.unselectable();
34238             o.enableDisplayMode("block");
34239             // all splitbars share the same overlay
34240             Roo.bootstrap.SplitBar.prototype.overlay = o;
34241         }
34242         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34243         this.overlay.show();
34244         Roo.get(this.proxy).setDisplayed("block");
34245         var size = this.adapter.getElementSize(this);
34246         this.activeMinSize = this.getMinimumSize();;
34247         this.activeMaxSize = this.getMaximumSize();;
34248         var c1 = size - this.activeMinSize;
34249         var c2 = Math.max(this.activeMaxSize - size, 0);
34250         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34251             this.dd.resetConstraints();
34252             this.dd.setXConstraint(
34253                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34254                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34255             );
34256             this.dd.setYConstraint(0, 0);
34257         }else{
34258             this.dd.resetConstraints();
34259             this.dd.setXConstraint(0, 0);
34260             this.dd.setYConstraint(
34261                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34262                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34263             );
34264          }
34265         this.dragSpecs.startSize = size;
34266         this.dragSpecs.startPoint = [x, y];
34267         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34268     },
34269     
34270     /** 
34271      * @private Called after the drag operation by the DDProxy
34272      */
34273     onEndProxyDrag : function(e){
34274         Roo.get(this.proxy).setDisplayed(false);
34275         var endPoint = Roo.lib.Event.getXY(e);
34276         if(this.overlay){
34277             this.overlay.hide();
34278         }
34279         var newSize;
34280         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34281             newSize = this.dragSpecs.startSize + 
34282                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34283                     endPoint[0] - this.dragSpecs.startPoint[0] :
34284                     this.dragSpecs.startPoint[0] - endPoint[0]
34285                 );
34286         }else{
34287             newSize = this.dragSpecs.startSize + 
34288                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34289                     endPoint[1] - this.dragSpecs.startPoint[1] :
34290                     this.dragSpecs.startPoint[1] - endPoint[1]
34291                 );
34292         }
34293         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34294         if(newSize != this.dragSpecs.startSize){
34295             if(this.fireEvent('beforeapply', this, newSize) !== false){
34296                 this.adapter.setElementSize(this, newSize);
34297                 this.fireEvent("moved", this, newSize);
34298                 this.fireEvent("resize", this, newSize);
34299             }
34300         }
34301     },
34302     
34303     /**
34304      * Get the adapter this SplitBar uses
34305      * @return The adapter object
34306      */
34307     getAdapter : function(){
34308         return this.adapter;
34309     },
34310     
34311     /**
34312      * Set the adapter this SplitBar uses
34313      * @param {Object} adapter A SplitBar adapter object
34314      */
34315     setAdapter : function(adapter){
34316         this.adapter = adapter;
34317         this.adapter.init(this);
34318     },
34319     
34320     /**
34321      * Gets the minimum size for the resizing element
34322      * @return {Number} The minimum size
34323      */
34324     getMinimumSize : function(){
34325         return this.minSize;
34326     },
34327     
34328     /**
34329      * Sets the minimum size for the resizing element
34330      * @param {Number} minSize The minimum size
34331      */
34332     setMinimumSize : function(minSize){
34333         this.minSize = minSize;
34334     },
34335     
34336     /**
34337      * Gets the maximum size for the resizing element
34338      * @return {Number} The maximum size
34339      */
34340     getMaximumSize : function(){
34341         return this.maxSize;
34342     },
34343     
34344     /**
34345      * Sets the maximum size for the resizing element
34346      * @param {Number} maxSize The maximum size
34347      */
34348     setMaximumSize : function(maxSize){
34349         this.maxSize = maxSize;
34350     },
34351     
34352     /**
34353      * Sets the initialize size for the resizing element
34354      * @param {Number} size The initial size
34355      */
34356     setCurrentSize : function(size){
34357         var oldAnimate = this.animate;
34358         this.animate = false;
34359         this.adapter.setElementSize(this, size);
34360         this.animate = oldAnimate;
34361     },
34362     
34363     /**
34364      * Destroy this splitbar. 
34365      * @param {Boolean} removeEl True to remove the element
34366      */
34367     destroy : function(removeEl){
34368         if(this.shim){
34369             this.shim.remove();
34370         }
34371         this.dd.unreg();
34372         this.proxy.parentNode.removeChild(this.proxy);
34373         if(removeEl){
34374             this.el.remove();
34375         }
34376     }
34377 });
34378
34379 /**
34380  * @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.
34381  */
34382 Roo.bootstrap.SplitBar.createProxy = function(dir){
34383     var proxy = new Roo.Element(document.createElement("div"));
34384     proxy.unselectable();
34385     var cls = 'roo-splitbar-proxy';
34386     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34387     document.body.appendChild(proxy.dom);
34388     return proxy.dom;
34389 };
34390
34391 /** 
34392  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34393  * Default Adapter. It assumes the splitter and resizing element are not positioned
34394  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34395  */
34396 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34397 };
34398
34399 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34400     // do nothing for now
34401     init : function(s){
34402     
34403     },
34404     /**
34405      * Called before drag operations to get the current size of the resizing element. 
34406      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34407      */
34408      getElementSize : function(s){
34409         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34410             return s.resizingEl.getWidth();
34411         }else{
34412             return s.resizingEl.getHeight();
34413         }
34414     },
34415     
34416     /**
34417      * Called after drag operations to set the size of the resizing element.
34418      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34419      * @param {Number} newSize The new size to set
34420      * @param {Function} onComplete A function to be invoked when resizing is complete
34421      */
34422     setElementSize : function(s, newSize, onComplete){
34423         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34424             if(!s.animate){
34425                 s.resizingEl.setWidth(newSize);
34426                 if(onComplete){
34427                     onComplete(s, newSize);
34428                 }
34429             }else{
34430                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34431             }
34432         }else{
34433             
34434             if(!s.animate){
34435                 s.resizingEl.setHeight(newSize);
34436                 if(onComplete){
34437                     onComplete(s, newSize);
34438                 }
34439             }else{
34440                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34441             }
34442         }
34443     }
34444 };
34445
34446 /** 
34447  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34448  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34449  * Adapter that  moves the splitter element to align with the resized sizing element. 
34450  * Used with an absolute positioned SplitBar.
34451  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34452  * document.body, make sure you assign an id to the body element.
34453  */
34454 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34455     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34456     this.container = Roo.get(container);
34457 };
34458
34459 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34460     init : function(s){
34461         this.basic.init(s);
34462     },
34463     
34464     getElementSize : function(s){
34465         return this.basic.getElementSize(s);
34466     },
34467     
34468     setElementSize : function(s, newSize, onComplete){
34469         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34470     },
34471     
34472     moveSplitter : function(s){
34473         var yes = Roo.bootstrap.SplitBar;
34474         switch(s.placement){
34475             case yes.LEFT:
34476                 s.el.setX(s.resizingEl.getRight());
34477                 break;
34478             case yes.RIGHT:
34479                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34480                 break;
34481             case yes.TOP:
34482                 s.el.setY(s.resizingEl.getBottom());
34483                 break;
34484             case yes.BOTTOM:
34485                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34486                 break;
34487         }
34488     }
34489 };
34490
34491 /**
34492  * Orientation constant - Create a vertical SplitBar
34493  * @static
34494  * @type Number
34495  */
34496 Roo.bootstrap.SplitBar.VERTICAL = 1;
34497
34498 /**
34499  * Orientation constant - Create a horizontal SplitBar
34500  * @static
34501  * @type Number
34502  */
34503 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34504
34505 /**
34506  * Placement constant - The resizing element is to the left of the splitter element
34507  * @static
34508  * @type Number
34509  */
34510 Roo.bootstrap.SplitBar.LEFT = 1;
34511
34512 /**
34513  * Placement constant - The resizing element is to the right of the splitter element
34514  * @static
34515  * @type Number
34516  */
34517 Roo.bootstrap.SplitBar.RIGHT = 2;
34518
34519 /**
34520  * Placement constant - The resizing element is positioned above the splitter element
34521  * @static
34522  * @type Number
34523  */
34524 Roo.bootstrap.SplitBar.TOP = 3;
34525
34526 /**
34527  * Placement constant - The resizing element is positioned under splitter element
34528  * @static
34529  * @type Number
34530  */
34531 Roo.bootstrap.SplitBar.BOTTOM = 4;
34532 Roo.namespace("Roo.bootstrap.layout");/*
34533  * Based on:
34534  * Ext JS Library 1.1.1
34535  * Copyright(c) 2006-2007, Ext JS, LLC.
34536  *
34537  * Originally Released Under LGPL - original licence link has changed is not relivant.
34538  *
34539  * Fork - LGPL
34540  * <script type="text/javascript">
34541  */
34542
34543 /**
34544  * @class Roo.bootstrap.layout.Manager
34545  * @extends Roo.bootstrap.Component
34546  * Base class for layout managers.
34547  */
34548 Roo.bootstrap.layout.Manager = function(config)
34549 {
34550     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34551
34552
34553
34554
34555
34556     /** false to disable window resize monitoring @type Boolean */
34557     this.monitorWindowResize = true;
34558     this.regions = {};
34559     this.addEvents({
34560         /**
34561          * @event layout
34562          * Fires when a layout is performed.
34563          * @param {Roo.LayoutManager} this
34564          */
34565         "layout" : true,
34566         /**
34567          * @event regionresized
34568          * Fires when the user resizes a region.
34569          * @param {Roo.LayoutRegion} region The resized region
34570          * @param {Number} newSize The new size (width for east/west, height for north/south)
34571          */
34572         "regionresized" : true,
34573         /**
34574          * @event regioncollapsed
34575          * Fires when a region is collapsed.
34576          * @param {Roo.LayoutRegion} region The collapsed region
34577          */
34578         "regioncollapsed" : true,
34579         /**
34580          * @event regionexpanded
34581          * Fires when a region is expanded.
34582          * @param {Roo.LayoutRegion} region The expanded region
34583          */
34584         "regionexpanded" : true
34585     });
34586     this.updating = false;
34587
34588     if (config.el) {
34589         this.el = Roo.get(config.el);
34590         this.initEvents();
34591     }
34592
34593 };
34594
34595 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34596
34597
34598     regions : null,
34599
34600     monitorWindowResize : true,
34601
34602
34603     updating : false,
34604
34605
34606     onRender : function(ct, position)
34607     {
34608         if(!this.el){
34609             this.el = Roo.get(ct);
34610             this.initEvents();
34611         }
34612         //this.fireEvent('render',this);
34613     },
34614
34615
34616     initEvents: function()
34617     {
34618
34619
34620         // ie scrollbar fix
34621         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34622             document.body.scroll = "no";
34623         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34624             this.el.position('relative');
34625         }
34626         this.id = this.el.id;
34627         this.el.addClass("roo-layout-container");
34628         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34629         if(this.el.dom != document.body ) {
34630             this.el.on('resize', this.layout,this);
34631             this.el.on('show', this.layout,this);
34632         }
34633
34634     },
34635
34636     /**
34637      * Returns true if this layout is currently being updated
34638      * @return {Boolean}
34639      */
34640     isUpdating : function(){
34641         return this.updating;
34642     },
34643
34644     /**
34645      * Suspend the LayoutManager from doing auto-layouts while
34646      * making multiple add or remove calls
34647      */
34648     beginUpdate : function(){
34649         this.updating = true;
34650     },
34651
34652     /**
34653      * Restore auto-layouts and optionally disable the manager from performing a layout
34654      * @param {Boolean} noLayout true to disable a layout update
34655      */
34656     endUpdate : function(noLayout){
34657         this.updating = false;
34658         if(!noLayout){
34659             this.layout();
34660         }
34661     },
34662
34663     layout: function(){
34664         // abstract...
34665     },
34666
34667     onRegionResized : function(region, newSize){
34668         this.fireEvent("regionresized", region, newSize);
34669         this.layout();
34670     },
34671
34672     onRegionCollapsed : function(region){
34673         this.fireEvent("regioncollapsed", region);
34674     },
34675
34676     onRegionExpanded : function(region){
34677         this.fireEvent("regionexpanded", region);
34678     },
34679
34680     /**
34681      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34682      * performs box-model adjustments.
34683      * @return {Object} The size as an object {width: (the width), height: (the height)}
34684      */
34685     getViewSize : function()
34686     {
34687         var size;
34688         if(this.el.dom != document.body){
34689             size = this.el.getSize();
34690         }else{
34691             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34692         }
34693         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34694         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34695         return size;
34696     },
34697
34698     /**
34699      * Returns the Element this layout is bound to.
34700      * @return {Roo.Element}
34701      */
34702     getEl : function(){
34703         return this.el;
34704     },
34705
34706     /**
34707      * Returns the specified region.
34708      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34709      * @return {Roo.LayoutRegion}
34710      */
34711     getRegion : function(target){
34712         return this.regions[target.toLowerCase()];
34713     },
34714
34715     onWindowResize : function(){
34716         if(this.monitorWindowResize){
34717             this.layout();
34718         }
34719     }
34720 });
34721 /*
34722  * Based on:
34723  * Ext JS Library 1.1.1
34724  * Copyright(c) 2006-2007, Ext JS, LLC.
34725  *
34726  * Originally Released Under LGPL - original licence link has changed is not relivant.
34727  *
34728  * Fork - LGPL
34729  * <script type="text/javascript">
34730  */
34731 /**
34732  * @class Roo.bootstrap.layout.Border
34733  * @extends Roo.bootstrap.layout.Manager
34734  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34735  * please see: examples/bootstrap/nested.html<br><br>
34736  
34737 <b>The container the layout is rendered into can be either the body element or any other element.
34738 If it is not the body element, the container needs to either be an absolute positioned element,
34739 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34740 the container size if it is not the body element.</b>
34741
34742 * @constructor
34743 * Create a new Border
34744 * @param {Object} config Configuration options
34745  */
34746 Roo.bootstrap.layout.Border = function(config){
34747     config = config || {};
34748     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34749     
34750     
34751     
34752     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34753         if(config[region]){
34754             config[region].region = region;
34755             this.addRegion(config[region]);
34756         }
34757     },this);
34758     
34759 };
34760
34761 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34762
34763 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34764     /**
34765      * Creates and adds a new region if it doesn't already exist.
34766      * @param {String} target The target region key (north, south, east, west or center).
34767      * @param {Object} config The regions config object
34768      * @return {BorderLayoutRegion} The new region
34769      */
34770     addRegion : function(config)
34771     {
34772         if(!this.regions[config.region]){
34773             var r = this.factory(config);
34774             this.bindRegion(r);
34775         }
34776         return this.regions[config.region];
34777     },
34778
34779     // private (kinda)
34780     bindRegion : function(r){
34781         this.regions[r.config.region] = r;
34782         
34783         r.on("visibilitychange",    this.layout, this);
34784         r.on("paneladded",          this.layout, this);
34785         r.on("panelremoved",        this.layout, this);
34786         r.on("invalidated",         this.layout, this);
34787         r.on("resized",             this.onRegionResized, this);
34788         r.on("collapsed",           this.onRegionCollapsed, this);
34789         r.on("expanded",            this.onRegionExpanded, this);
34790     },
34791
34792     /**
34793      * Performs a layout update.
34794      */
34795     layout : function()
34796     {
34797         if(this.updating) {
34798             return;
34799         }
34800         
34801         // render all the rebions if they have not been done alreayd?
34802         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34803             if(this.regions[region] && !this.regions[region].bodyEl){
34804                 this.regions[region].onRender(this.el)
34805             }
34806         },this);
34807         
34808         var size = this.getViewSize();
34809         var w = size.width;
34810         var h = size.height;
34811         var centerW = w;
34812         var centerH = h;
34813         var centerY = 0;
34814         var centerX = 0;
34815         //var x = 0, y = 0;
34816
34817         var rs = this.regions;
34818         var north = rs["north"];
34819         var south = rs["south"]; 
34820         var west = rs["west"];
34821         var east = rs["east"];
34822         var center = rs["center"];
34823         //if(this.hideOnLayout){ // not supported anymore
34824             //c.el.setStyle("display", "none");
34825         //}
34826         if(north && north.isVisible()){
34827             var b = north.getBox();
34828             var m = north.getMargins();
34829             b.width = w - (m.left+m.right);
34830             b.x = m.left;
34831             b.y = m.top;
34832             centerY = b.height + b.y + m.bottom;
34833             centerH -= centerY;
34834             north.updateBox(this.safeBox(b));
34835         }
34836         if(south && south.isVisible()){
34837             var b = south.getBox();
34838             var m = south.getMargins();
34839             b.width = w - (m.left+m.right);
34840             b.x = m.left;
34841             var totalHeight = (b.height + m.top + m.bottom);
34842             b.y = h - totalHeight + m.top;
34843             centerH -= totalHeight;
34844             south.updateBox(this.safeBox(b));
34845         }
34846         if(west && west.isVisible()){
34847             var b = west.getBox();
34848             var m = west.getMargins();
34849             b.height = centerH - (m.top+m.bottom);
34850             b.x = m.left;
34851             b.y = centerY + m.top;
34852             var totalWidth = (b.width + m.left + m.right);
34853             centerX += totalWidth;
34854             centerW -= totalWidth;
34855             west.updateBox(this.safeBox(b));
34856         }
34857         if(east && east.isVisible()){
34858             var b = east.getBox();
34859             var m = east.getMargins();
34860             b.height = centerH - (m.top+m.bottom);
34861             var totalWidth = (b.width + m.left + m.right);
34862             b.x = w - totalWidth + m.left;
34863             b.y = centerY + m.top;
34864             centerW -= totalWidth;
34865             east.updateBox(this.safeBox(b));
34866         }
34867         if(center){
34868             var m = center.getMargins();
34869             var centerBox = {
34870                 x: centerX + m.left,
34871                 y: centerY + m.top,
34872                 width: centerW - (m.left+m.right),
34873                 height: centerH - (m.top+m.bottom)
34874             };
34875             //if(this.hideOnLayout){
34876                 //center.el.setStyle("display", "block");
34877             //}
34878             center.updateBox(this.safeBox(centerBox));
34879         }
34880         this.el.repaint();
34881         this.fireEvent("layout", this);
34882     },
34883
34884     // private
34885     safeBox : function(box){
34886         box.width = Math.max(0, box.width);
34887         box.height = Math.max(0, box.height);
34888         return box;
34889     },
34890
34891     /**
34892      * Adds a ContentPanel (or subclass) to this layout.
34893      * @param {String} target The target region key (north, south, east, west or center).
34894      * @param {Roo.ContentPanel} panel The panel to add
34895      * @return {Roo.ContentPanel} The added panel
34896      */
34897     add : function(target, panel){
34898          
34899         target = target.toLowerCase();
34900         return this.regions[target].add(panel);
34901     },
34902
34903     /**
34904      * Remove a ContentPanel (or subclass) to this layout.
34905      * @param {String} target The target region key (north, south, east, west or center).
34906      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34907      * @return {Roo.ContentPanel} The removed panel
34908      */
34909     remove : function(target, panel){
34910         target = target.toLowerCase();
34911         return this.regions[target].remove(panel);
34912     },
34913
34914     /**
34915      * Searches all regions for a panel with the specified id
34916      * @param {String} panelId
34917      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34918      */
34919     findPanel : function(panelId){
34920         var rs = this.regions;
34921         for(var target in rs){
34922             if(typeof rs[target] != "function"){
34923                 var p = rs[target].getPanel(panelId);
34924                 if(p){
34925                     return p;
34926                 }
34927             }
34928         }
34929         return null;
34930     },
34931
34932     /**
34933      * Searches all regions for a panel with the specified id and activates (shows) it.
34934      * @param {String/ContentPanel} panelId The panels id or the panel itself
34935      * @return {Roo.ContentPanel} The shown panel or null
34936      */
34937     showPanel : function(panelId) {
34938       var rs = this.regions;
34939       for(var target in rs){
34940          var r = rs[target];
34941          if(typeof r != "function"){
34942             if(r.hasPanel(panelId)){
34943                return r.showPanel(panelId);
34944             }
34945          }
34946       }
34947       return null;
34948    },
34949
34950    /**
34951      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34952      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34953      */
34954    /*
34955     restoreState : function(provider){
34956         if(!provider){
34957             provider = Roo.state.Manager;
34958         }
34959         var sm = new Roo.LayoutStateManager();
34960         sm.init(this, provider);
34961     },
34962 */
34963  
34964  
34965     /**
34966      * Adds a xtype elements to the layout.
34967      * <pre><code>
34968
34969 layout.addxtype({
34970        xtype : 'ContentPanel',
34971        region: 'west',
34972        items: [ .... ]
34973    }
34974 );
34975
34976 layout.addxtype({
34977         xtype : 'NestedLayoutPanel',
34978         region: 'west',
34979         layout: {
34980            center: { },
34981            west: { }   
34982         },
34983         items : [ ... list of content panels or nested layout panels.. ]
34984    }
34985 );
34986 </code></pre>
34987      * @param {Object} cfg Xtype definition of item to add.
34988      */
34989     addxtype : function(cfg)
34990     {
34991         // basically accepts a pannel...
34992         // can accept a layout region..!?!?
34993         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34994         
34995         
34996         // theory?  children can only be panels??
34997         
34998         //if (!cfg.xtype.match(/Panel$/)) {
34999         //    return false;
35000         //}
35001         var ret = false;
35002         
35003         if (typeof(cfg.region) == 'undefined') {
35004             Roo.log("Failed to add Panel, region was not set");
35005             Roo.log(cfg);
35006             return false;
35007         }
35008         var region = cfg.region;
35009         delete cfg.region;
35010         
35011           
35012         var xitems = [];
35013         if (cfg.items) {
35014             xitems = cfg.items;
35015             delete cfg.items;
35016         }
35017         var nb = false;
35018         
35019         switch(cfg.xtype) 
35020         {
35021             case 'Content':  // ContentPanel (el, cfg)
35022             case 'Scroll':  // ContentPanel (el, cfg)
35023             case 'View': 
35024                 cfg.autoCreate = true;
35025                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35026                 //} else {
35027                 //    var el = this.el.createChild();
35028                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35029                 //}
35030                 
35031                 this.add(region, ret);
35032                 break;
35033             
35034             /*
35035             case 'TreePanel': // our new panel!
35036                 cfg.el = this.el.createChild();
35037                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35038                 this.add(region, ret);
35039                 break;
35040             */
35041             
35042             case 'Nest': 
35043                 // create a new Layout (which is  a Border Layout...
35044                 
35045                 var clayout = cfg.layout;
35046                 clayout.el  = this.el.createChild();
35047                 clayout.items   = clayout.items  || [];
35048                 
35049                 delete cfg.layout;
35050                 
35051                 // replace this exitems with the clayout ones..
35052                 xitems = clayout.items;
35053                  
35054                 // force background off if it's in center...
35055                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35056                     cfg.background = false;
35057                 }
35058                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35059                 
35060                 
35061                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35062                 //console.log('adding nested layout panel '  + cfg.toSource());
35063                 this.add(region, ret);
35064                 nb = {}; /// find first...
35065                 break;
35066             
35067             case 'Grid':
35068                 
35069                 // needs grid and region
35070                 
35071                 //var el = this.getRegion(region).el.createChild();
35072                 /*
35073                  *var el = this.el.createChild();
35074                 // create the grid first...
35075                 cfg.grid.container = el;
35076                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35077                 */
35078                 
35079                 if (region == 'center' && this.active ) {
35080                     cfg.background = false;
35081                 }
35082                 
35083                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35084                 
35085                 this.add(region, ret);
35086                 /*
35087                 if (cfg.background) {
35088                     // render grid on panel activation (if panel background)
35089                     ret.on('activate', function(gp) {
35090                         if (!gp.grid.rendered) {
35091                     //        gp.grid.render(el);
35092                         }
35093                     });
35094                 } else {
35095                   //  cfg.grid.render(el);
35096                 }
35097                 */
35098                 break;
35099            
35100            
35101             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35102                 // it was the old xcomponent building that caused this before.
35103                 // espeically if border is the top element in the tree.
35104                 ret = this;
35105                 break; 
35106                 
35107                     
35108                 
35109                 
35110                 
35111             default:
35112                 /*
35113                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35114                     
35115                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35116                     this.add(region, ret);
35117                 } else {
35118                 */
35119                     Roo.log(cfg);
35120                     throw "Can not add '" + cfg.xtype + "' to Border";
35121                     return null;
35122              
35123                                 
35124              
35125         }
35126         this.beginUpdate();
35127         // add children..
35128         var region = '';
35129         var abn = {};
35130         Roo.each(xitems, function(i)  {
35131             region = nb && i.region ? i.region : false;
35132             
35133             var add = ret.addxtype(i);
35134            
35135             if (region) {
35136                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35137                 if (!i.background) {
35138                     abn[region] = nb[region] ;
35139                 }
35140             }
35141             
35142         });
35143         this.endUpdate();
35144
35145         // make the last non-background panel active..
35146         //if (nb) { Roo.log(abn); }
35147         if (nb) {
35148             
35149             for(var r in abn) {
35150                 region = this.getRegion(r);
35151                 if (region) {
35152                     // tried using nb[r], but it does not work..
35153                      
35154                     region.showPanel(abn[r]);
35155                    
35156                 }
35157             }
35158         }
35159         return ret;
35160         
35161     },
35162     
35163     
35164 // private
35165     factory : function(cfg)
35166     {
35167         
35168         var validRegions = Roo.bootstrap.layout.Border.regions;
35169
35170         var target = cfg.region;
35171         cfg.mgr = this;
35172         
35173         var r = Roo.bootstrap.layout;
35174         Roo.log(target);
35175         switch(target){
35176             case "north":
35177                 return new r.North(cfg);
35178             case "south":
35179                 return new r.South(cfg);
35180             case "east":
35181                 return new r.East(cfg);
35182             case "west":
35183                 return new r.West(cfg);
35184             case "center":
35185                 return new r.Center(cfg);
35186         }
35187         throw 'Layout region "'+target+'" not supported.';
35188     }
35189     
35190     
35191 });
35192  /*
35193  * Based on:
35194  * Ext JS Library 1.1.1
35195  * Copyright(c) 2006-2007, Ext JS, LLC.
35196  *
35197  * Originally Released Under LGPL - original licence link has changed is not relivant.
35198  *
35199  * Fork - LGPL
35200  * <script type="text/javascript">
35201  */
35202  
35203 /**
35204  * @class Roo.bootstrap.layout.Basic
35205  * @extends Roo.util.Observable
35206  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35207  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35208  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35209  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35210  * @cfg {string}   region  the region that it inhabits..
35211  * @cfg {bool}   skipConfig skip config?
35212  * 
35213
35214  */
35215 Roo.bootstrap.layout.Basic = function(config){
35216     
35217     this.mgr = config.mgr;
35218     
35219     this.position = config.region;
35220     
35221     var skipConfig = config.skipConfig;
35222     
35223     this.events = {
35224         /**
35225          * @scope Roo.BasicLayoutRegion
35226          */
35227         
35228         /**
35229          * @event beforeremove
35230          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35231          * @param {Roo.LayoutRegion} this
35232          * @param {Roo.ContentPanel} panel The panel
35233          * @param {Object} e The cancel event object
35234          */
35235         "beforeremove" : true,
35236         /**
35237          * @event invalidated
35238          * Fires when the layout for this region is changed.
35239          * @param {Roo.LayoutRegion} this
35240          */
35241         "invalidated" : true,
35242         /**
35243          * @event visibilitychange
35244          * Fires when this region is shown or hidden 
35245          * @param {Roo.LayoutRegion} this
35246          * @param {Boolean} visibility true or false
35247          */
35248         "visibilitychange" : true,
35249         /**
35250          * @event paneladded
35251          * Fires when a panel is added. 
35252          * @param {Roo.LayoutRegion} this
35253          * @param {Roo.ContentPanel} panel The panel
35254          */
35255         "paneladded" : true,
35256         /**
35257          * @event panelremoved
35258          * Fires when a panel is removed. 
35259          * @param {Roo.LayoutRegion} this
35260          * @param {Roo.ContentPanel} panel The panel
35261          */
35262         "panelremoved" : true,
35263         /**
35264          * @event beforecollapse
35265          * Fires when this region before collapse.
35266          * @param {Roo.LayoutRegion} this
35267          */
35268         "beforecollapse" : true,
35269         /**
35270          * @event collapsed
35271          * Fires when this region is collapsed.
35272          * @param {Roo.LayoutRegion} this
35273          */
35274         "collapsed" : true,
35275         /**
35276          * @event expanded
35277          * Fires when this region is expanded.
35278          * @param {Roo.LayoutRegion} this
35279          */
35280         "expanded" : true,
35281         /**
35282          * @event slideshow
35283          * Fires when this region is slid into view.
35284          * @param {Roo.LayoutRegion} this
35285          */
35286         "slideshow" : true,
35287         /**
35288          * @event slidehide
35289          * Fires when this region slides out of view. 
35290          * @param {Roo.LayoutRegion} this
35291          */
35292         "slidehide" : true,
35293         /**
35294          * @event panelactivated
35295          * Fires when a panel is activated. 
35296          * @param {Roo.LayoutRegion} this
35297          * @param {Roo.ContentPanel} panel The activated panel
35298          */
35299         "panelactivated" : true,
35300         /**
35301          * @event resized
35302          * Fires when the user resizes this region. 
35303          * @param {Roo.LayoutRegion} this
35304          * @param {Number} newSize The new size (width for east/west, height for north/south)
35305          */
35306         "resized" : true
35307     };
35308     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35309     this.panels = new Roo.util.MixedCollection();
35310     this.panels.getKey = this.getPanelId.createDelegate(this);
35311     this.box = null;
35312     this.activePanel = null;
35313     // ensure listeners are added...
35314     
35315     if (config.listeners || config.events) {
35316         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35317             listeners : config.listeners || {},
35318             events : config.events || {}
35319         });
35320     }
35321     
35322     if(skipConfig !== true){
35323         this.applyConfig(config);
35324     }
35325 };
35326
35327 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35328 {
35329     getPanelId : function(p){
35330         return p.getId();
35331     },
35332     
35333     applyConfig : function(config){
35334         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35335         this.config = config;
35336         
35337     },
35338     
35339     /**
35340      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35341      * the width, for horizontal (north, south) the height.
35342      * @param {Number} newSize The new width or height
35343      */
35344     resizeTo : function(newSize){
35345         var el = this.el ? this.el :
35346                  (this.activePanel ? this.activePanel.getEl() : null);
35347         if(el){
35348             switch(this.position){
35349                 case "east":
35350                 case "west":
35351                     el.setWidth(newSize);
35352                     this.fireEvent("resized", this, newSize);
35353                 break;
35354                 case "north":
35355                 case "south":
35356                     el.setHeight(newSize);
35357                     this.fireEvent("resized", this, newSize);
35358                 break;                
35359             }
35360         }
35361     },
35362     
35363     getBox : function(){
35364         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35365     },
35366     
35367     getMargins : function(){
35368         return this.margins;
35369     },
35370     
35371     updateBox : function(box){
35372         this.box = box;
35373         var el = this.activePanel.getEl();
35374         el.dom.style.left = box.x + "px";
35375         el.dom.style.top = box.y + "px";
35376         this.activePanel.setSize(box.width, box.height);
35377     },
35378     
35379     /**
35380      * Returns the container element for this region.
35381      * @return {Roo.Element}
35382      */
35383     getEl : function(){
35384         return this.activePanel;
35385     },
35386     
35387     /**
35388      * Returns true if this region is currently visible.
35389      * @return {Boolean}
35390      */
35391     isVisible : function(){
35392         return this.activePanel ? true : false;
35393     },
35394     
35395     setActivePanel : function(panel){
35396         panel = this.getPanel(panel);
35397         if(this.activePanel && this.activePanel != panel){
35398             this.activePanel.setActiveState(false);
35399             this.activePanel.getEl().setLeftTop(-10000,-10000);
35400         }
35401         this.activePanel = panel;
35402         panel.setActiveState(true);
35403         if(this.box){
35404             panel.setSize(this.box.width, this.box.height);
35405         }
35406         this.fireEvent("panelactivated", this, panel);
35407         this.fireEvent("invalidated");
35408     },
35409     
35410     /**
35411      * Show the specified panel.
35412      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35413      * @return {Roo.ContentPanel} The shown panel or null
35414      */
35415     showPanel : function(panel){
35416         panel = this.getPanel(panel);
35417         if(panel){
35418             this.setActivePanel(panel);
35419         }
35420         return panel;
35421     },
35422     
35423     /**
35424      * Get the active panel for this region.
35425      * @return {Roo.ContentPanel} The active panel or null
35426      */
35427     getActivePanel : function(){
35428         return this.activePanel;
35429     },
35430     
35431     /**
35432      * Add the passed ContentPanel(s)
35433      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35434      * @return {Roo.ContentPanel} The panel added (if only one was added)
35435      */
35436     add : function(panel){
35437         if(arguments.length > 1){
35438             for(var i = 0, len = arguments.length; i < len; i++) {
35439                 this.add(arguments[i]);
35440             }
35441             return null;
35442         }
35443         if(this.hasPanel(panel)){
35444             this.showPanel(panel);
35445             return panel;
35446         }
35447         var el = panel.getEl();
35448         if(el.dom.parentNode != this.mgr.el.dom){
35449             this.mgr.el.dom.appendChild(el.dom);
35450         }
35451         if(panel.setRegion){
35452             panel.setRegion(this);
35453         }
35454         this.panels.add(panel);
35455         el.setStyle("position", "absolute");
35456         if(!panel.background){
35457             this.setActivePanel(panel);
35458             if(this.config.initialSize && this.panels.getCount()==1){
35459                 this.resizeTo(this.config.initialSize);
35460             }
35461         }
35462         this.fireEvent("paneladded", this, panel);
35463         return panel;
35464     },
35465     
35466     /**
35467      * Returns true if the panel is in this region.
35468      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35469      * @return {Boolean}
35470      */
35471     hasPanel : function(panel){
35472         if(typeof panel == "object"){ // must be panel obj
35473             panel = panel.getId();
35474         }
35475         return this.getPanel(panel) ? true : false;
35476     },
35477     
35478     /**
35479      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35480      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35481      * @param {Boolean} preservePanel Overrides the config preservePanel option
35482      * @return {Roo.ContentPanel} The panel that was removed
35483      */
35484     remove : function(panel, preservePanel){
35485         panel = this.getPanel(panel);
35486         if(!panel){
35487             return null;
35488         }
35489         var e = {};
35490         this.fireEvent("beforeremove", this, panel, e);
35491         if(e.cancel === true){
35492             return null;
35493         }
35494         var panelId = panel.getId();
35495         this.panels.removeKey(panelId);
35496         return panel;
35497     },
35498     
35499     /**
35500      * Returns the panel specified or null if it's not in this region.
35501      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35502      * @return {Roo.ContentPanel}
35503      */
35504     getPanel : function(id){
35505         if(typeof id == "object"){ // must be panel obj
35506             return id;
35507         }
35508         return this.panels.get(id);
35509     },
35510     
35511     /**
35512      * Returns this regions position (north/south/east/west/center).
35513      * @return {String} 
35514      */
35515     getPosition: function(){
35516         return this.position;    
35517     }
35518 });/*
35519  * Based on:
35520  * Ext JS Library 1.1.1
35521  * Copyright(c) 2006-2007, Ext JS, LLC.
35522  *
35523  * Originally Released Under LGPL - original licence link has changed is not relivant.
35524  *
35525  * Fork - LGPL
35526  * <script type="text/javascript">
35527  */
35528  
35529 /**
35530  * @class Roo.bootstrap.layout.Region
35531  * @extends Roo.bootstrap.layout.Basic
35532  * This class represents a region in a layout manager.
35533  
35534  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35535  * @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})
35536  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35537  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35538  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35539  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35540  * @cfg {String}    title           The title for the region (overrides panel titles)
35541  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35542  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35543  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35544  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35545  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35546  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35547  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35548  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35549  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35550  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35551
35552  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35553  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35554  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35555  * @cfg {Number}    width           For East/West panels
35556  * @cfg {Number}    height          For North/South panels
35557  * @cfg {Boolean}   split           To show the splitter
35558  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35559  * 
35560  * @cfg {string}   cls             Extra CSS classes to add to region
35561  * 
35562  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35563  * @cfg {string}   region  the region that it inhabits..
35564  *
35565
35566  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35567  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35568
35569  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35570  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35571  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35572  */
35573 Roo.bootstrap.layout.Region = function(config)
35574 {
35575     this.applyConfig(config);
35576
35577     var mgr = config.mgr;
35578     var pos = config.region;
35579     config.skipConfig = true;
35580     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35581     
35582     if (mgr.el) {
35583         this.onRender(mgr.el);   
35584     }
35585      
35586     this.visible = true;
35587     this.collapsed = false;
35588     this.unrendered_panels = [];
35589 };
35590
35591 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35592
35593     position: '', // set by wrapper (eg. north/south etc..)
35594     unrendered_panels : null,  // unrendered panels.
35595     createBody : function(){
35596         /** This region's body element 
35597         * @type Roo.Element */
35598         this.bodyEl = this.el.createChild({
35599                 tag: "div",
35600                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35601         });
35602     },
35603
35604     onRender: function(ctr, pos)
35605     {
35606         var dh = Roo.DomHelper;
35607         /** This region's container element 
35608         * @type Roo.Element */
35609         this.el = dh.append(ctr.dom, {
35610                 tag: "div",
35611                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35612             }, true);
35613         /** This region's title element 
35614         * @type Roo.Element */
35615     
35616         this.titleEl = dh.append(this.el.dom,
35617             {
35618                     tag: "div",
35619                     unselectable: "on",
35620                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35621                     children:[
35622                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35623                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35624                     ]}, true);
35625         
35626         this.titleEl.enableDisplayMode();
35627         /** This region's title text element 
35628         * @type HTMLElement */
35629         this.titleTextEl = this.titleEl.dom.firstChild;
35630         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35631         /*
35632         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35633         this.closeBtn.enableDisplayMode();
35634         this.closeBtn.on("click", this.closeClicked, this);
35635         this.closeBtn.hide();
35636     */
35637         this.createBody(this.config);
35638         if(this.config.hideWhenEmpty){
35639             this.hide();
35640             this.on("paneladded", this.validateVisibility, this);
35641             this.on("panelremoved", this.validateVisibility, this);
35642         }
35643         if(this.autoScroll){
35644             this.bodyEl.setStyle("overflow", "auto");
35645         }else{
35646             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35647         }
35648         //if(c.titlebar !== false){
35649             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35650                 this.titleEl.hide();
35651             }else{
35652                 this.titleEl.show();
35653                 if(this.config.title){
35654                     this.titleTextEl.innerHTML = this.config.title;
35655                 }
35656             }
35657         //}
35658         if(this.config.collapsed){
35659             this.collapse(true);
35660         }
35661         if(this.config.hidden){
35662             this.hide();
35663         }
35664         
35665         if (this.unrendered_panels && this.unrendered_panels.length) {
35666             for (var i =0;i< this.unrendered_panels.length; i++) {
35667                 this.add(this.unrendered_panels[i]);
35668             }
35669             this.unrendered_panels = null;
35670             
35671         }
35672         
35673     },
35674     
35675     applyConfig : function(c)
35676     {
35677         /*
35678          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35679             var dh = Roo.DomHelper;
35680             if(c.titlebar !== false){
35681                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35682                 this.collapseBtn.on("click", this.collapse, this);
35683                 this.collapseBtn.enableDisplayMode();
35684                 /*
35685                 if(c.showPin === true || this.showPin){
35686                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35687                     this.stickBtn.enableDisplayMode();
35688                     this.stickBtn.on("click", this.expand, this);
35689                     this.stickBtn.hide();
35690                 }
35691                 
35692             }
35693             */
35694             /** This region's collapsed element
35695             * @type Roo.Element */
35696             /*
35697              *
35698             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35699                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35700             ]}, true);
35701             
35702             if(c.floatable !== false){
35703                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35704                this.collapsedEl.on("click", this.collapseClick, this);
35705             }
35706
35707             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35708                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35709                    id: "message", unselectable: "on", style:{"float":"left"}});
35710                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35711              }
35712             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35713             this.expandBtn.on("click", this.expand, this);
35714             
35715         }
35716         
35717         if(this.collapseBtn){
35718             this.collapseBtn.setVisible(c.collapsible == true);
35719         }
35720         
35721         this.cmargins = c.cmargins || this.cmargins ||
35722                          (this.position == "west" || this.position == "east" ?
35723                              {top: 0, left: 2, right:2, bottom: 0} :
35724                              {top: 2, left: 0, right:0, bottom: 2});
35725         */
35726         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35727         
35728         
35729         this.bottomTabs = c.tabPosition != "top";
35730         
35731         this.autoScroll = c.autoScroll || false;
35732         
35733         
35734        
35735         
35736         this.duration = c.duration || .30;
35737         this.slideDuration = c.slideDuration || .45;
35738         this.config = c;
35739        
35740     },
35741     /**
35742      * Returns true if this region is currently visible.
35743      * @return {Boolean}
35744      */
35745     isVisible : function(){
35746         return this.visible;
35747     },
35748
35749     /**
35750      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35751      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35752      */
35753     //setCollapsedTitle : function(title){
35754     //    title = title || "&#160;";
35755      //   if(this.collapsedTitleTextEl){
35756       //      this.collapsedTitleTextEl.innerHTML = title;
35757        // }
35758     //},
35759
35760     getBox : function(){
35761         var b;
35762       //  if(!this.collapsed){
35763             b = this.el.getBox(false, true);
35764        // }else{
35765           //  b = this.collapsedEl.getBox(false, true);
35766         //}
35767         return b;
35768     },
35769
35770     getMargins : function(){
35771         return this.margins;
35772         //return this.collapsed ? this.cmargins : this.margins;
35773     },
35774 /*
35775     highlight : function(){
35776         this.el.addClass("x-layout-panel-dragover");
35777     },
35778
35779     unhighlight : function(){
35780         this.el.removeClass("x-layout-panel-dragover");
35781     },
35782 */
35783     updateBox : function(box)
35784     {
35785         if (!this.bodyEl) {
35786             return; // not rendered yet..
35787         }
35788         
35789         this.box = box;
35790         if(!this.collapsed){
35791             this.el.dom.style.left = box.x + "px";
35792             this.el.dom.style.top = box.y + "px";
35793             this.updateBody(box.width, box.height);
35794         }else{
35795             this.collapsedEl.dom.style.left = box.x + "px";
35796             this.collapsedEl.dom.style.top = box.y + "px";
35797             this.collapsedEl.setSize(box.width, box.height);
35798         }
35799         if(this.tabs){
35800             this.tabs.autoSizeTabs();
35801         }
35802     },
35803
35804     updateBody : function(w, h)
35805     {
35806         if(w !== null){
35807             this.el.setWidth(w);
35808             w -= this.el.getBorderWidth("rl");
35809             if(this.config.adjustments){
35810                 w += this.config.adjustments[0];
35811             }
35812         }
35813         if(h !== null && h > 0){
35814             this.el.setHeight(h);
35815             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35816             h -= this.el.getBorderWidth("tb");
35817             if(this.config.adjustments){
35818                 h += this.config.adjustments[1];
35819             }
35820             this.bodyEl.setHeight(h);
35821             if(this.tabs){
35822                 h = this.tabs.syncHeight(h);
35823             }
35824         }
35825         if(this.panelSize){
35826             w = w !== null ? w : this.panelSize.width;
35827             h = h !== null ? h : this.panelSize.height;
35828         }
35829         if(this.activePanel){
35830             var el = this.activePanel.getEl();
35831             w = w !== null ? w : el.getWidth();
35832             h = h !== null ? h : el.getHeight();
35833             this.panelSize = {width: w, height: h};
35834             this.activePanel.setSize(w, h);
35835         }
35836         if(Roo.isIE && this.tabs){
35837             this.tabs.el.repaint();
35838         }
35839     },
35840
35841     /**
35842      * Returns the container element for this region.
35843      * @return {Roo.Element}
35844      */
35845     getEl : function(){
35846         return this.el;
35847     },
35848
35849     /**
35850      * Hides this region.
35851      */
35852     hide : function(){
35853         //if(!this.collapsed){
35854             this.el.dom.style.left = "-2000px";
35855             this.el.hide();
35856         //}else{
35857          //   this.collapsedEl.dom.style.left = "-2000px";
35858          //   this.collapsedEl.hide();
35859        // }
35860         this.visible = false;
35861         this.fireEvent("visibilitychange", this, false);
35862     },
35863
35864     /**
35865      * Shows this region if it was previously hidden.
35866      */
35867     show : function(){
35868         //if(!this.collapsed){
35869             this.el.show();
35870         //}else{
35871         //    this.collapsedEl.show();
35872        // }
35873         this.visible = true;
35874         this.fireEvent("visibilitychange", this, true);
35875     },
35876 /*
35877     closeClicked : function(){
35878         if(this.activePanel){
35879             this.remove(this.activePanel);
35880         }
35881     },
35882
35883     collapseClick : function(e){
35884         if(this.isSlid){
35885            e.stopPropagation();
35886            this.slideIn();
35887         }else{
35888            e.stopPropagation();
35889            this.slideOut();
35890         }
35891     },
35892 */
35893     /**
35894      * Collapses this region.
35895      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35896      */
35897     /*
35898     collapse : function(skipAnim, skipCheck = false){
35899         if(this.collapsed) {
35900             return;
35901         }
35902         
35903         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35904             
35905             this.collapsed = true;
35906             if(this.split){
35907                 this.split.el.hide();
35908             }
35909             if(this.config.animate && skipAnim !== true){
35910                 this.fireEvent("invalidated", this);
35911                 this.animateCollapse();
35912             }else{
35913                 this.el.setLocation(-20000,-20000);
35914                 this.el.hide();
35915                 this.collapsedEl.show();
35916                 this.fireEvent("collapsed", this);
35917                 this.fireEvent("invalidated", this);
35918             }
35919         }
35920         
35921     },
35922 */
35923     animateCollapse : function(){
35924         // overridden
35925     },
35926
35927     /**
35928      * Expands this region if it was previously collapsed.
35929      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35930      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35931      */
35932     /*
35933     expand : function(e, skipAnim){
35934         if(e) {
35935             e.stopPropagation();
35936         }
35937         if(!this.collapsed || this.el.hasActiveFx()) {
35938             return;
35939         }
35940         if(this.isSlid){
35941             this.afterSlideIn();
35942             skipAnim = true;
35943         }
35944         this.collapsed = false;
35945         if(this.config.animate && skipAnim !== true){
35946             this.animateExpand();
35947         }else{
35948             this.el.show();
35949             if(this.split){
35950                 this.split.el.show();
35951             }
35952             this.collapsedEl.setLocation(-2000,-2000);
35953             this.collapsedEl.hide();
35954             this.fireEvent("invalidated", this);
35955             this.fireEvent("expanded", this);
35956         }
35957     },
35958 */
35959     animateExpand : function(){
35960         // overridden
35961     },
35962
35963     initTabs : function()
35964     {
35965         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35966         
35967         var ts = new Roo.bootstrap.panel.Tabs({
35968                 el: this.bodyEl.dom,
35969                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35970                 disableTooltips: this.config.disableTabTips,
35971                 toolbar : this.config.toolbar
35972             });
35973         
35974         if(this.config.hideTabs){
35975             ts.stripWrap.setDisplayed(false);
35976         }
35977         this.tabs = ts;
35978         ts.resizeTabs = this.config.resizeTabs === true;
35979         ts.minTabWidth = this.config.minTabWidth || 40;
35980         ts.maxTabWidth = this.config.maxTabWidth || 250;
35981         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35982         ts.monitorResize = false;
35983         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35984         ts.bodyEl.addClass('roo-layout-tabs-body');
35985         this.panels.each(this.initPanelAsTab, this);
35986     },
35987
35988     initPanelAsTab : function(panel){
35989         var ti = this.tabs.addTab(
35990             panel.getEl().id,
35991             panel.getTitle(),
35992             null,
35993             this.config.closeOnTab && panel.isClosable(),
35994             panel.tpl
35995         );
35996         if(panel.tabTip !== undefined){
35997             ti.setTooltip(panel.tabTip);
35998         }
35999         ti.on("activate", function(){
36000               this.setActivePanel(panel);
36001         }, this);
36002         
36003         if(this.config.closeOnTab){
36004             ti.on("beforeclose", function(t, e){
36005                 e.cancel = true;
36006                 this.remove(panel);
36007             }, this);
36008         }
36009         
36010         panel.tabItem = ti;
36011         
36012         return ti;
36013     },
36014
36015     updatePanelTitle : function(panel, title)
36016     {
36017         if(this.activePanel == panel){
36018             this.updateTitle(title);
36019         }
36020         if(this.tabs){
36021             var ti = this.tabs.getTab(panel.getEl().id);
36022             ti.setText(title);
36023             if(panel.tabTip !== undefined){
36024                 ti.setTooltip(panel.tabTip);
36025             }
36026         }
36027     },
36028
36029     updateTitle : function(title){
36030         if(this.titleTextEl && !this.config.title){
36031             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36032         }
36033     },
36034
36035     setActivePanel : function(panel)
36036     {
36037         panel = this.getPanel(panel);
36038         if(this.activePanel && this.activePanel != panel){
36039             if(this.activePanel.setActiveState(false) === false){
36040                 return;
36041             }
36042         }
36043         this.activePanel = panel;
36044         panel.setActiveState(true);
36045         if(this.panelSize){
36046             panel.setSize(this.panelSize.width, this.panelSize.height);
36047         }
36048         if(this.closeBtn){
36049             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36050         }
36051         this.updateTitle(panel.getTitle());
36052         if(this.tabs){
36053             this.fireEvent("invalidated", this);
36054         }
36055         this.fireEvent("panelactivated", this, panel);
36056     },
36057
36058     /**
36059      * Shows the specified panel.
36060      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36061      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36062      */
36063     showPanel : function(panel)
36064     {
36065         panel = this.getPanel(panel);
36066         if(panel){
36067             if(this.tabs){
36068                 var tab = this.tabs.getTab(panel.getEl().id);
36069                 if(tab.isHidden()){
36070                     this.tabs.unhideTab(tab.id);
36071                 }
36072                 tab.activate();
36073             }else{
36074                 this.setActivePanel(panel);
36075             }
36076         }
36077         return panel;
36078     },
36079
36080     /**
36081      * Get the active panel for this region.
36082      * @return {Roo.ContentPanel} The active panel or null
36083      */
36084     getActivePanel : function(){
36085         return this.activePanel;
36086     },
36087
36088     validateVisibility : function(){
36089         if(this.panels.getCount() < 1){
36090             this.updateTitle("&#160;");
36091             this.closeBtn.hide();
36092             this.hide();
36093         }else{
36094             if(!this.isVisible()){
36095                 this.show();
36096             }
36097         }
36098     },
36099
36100     /**
36101      * Adds the passed ContentPanel(s) to this region.
36102      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36103      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36104      */
36105     add : function(panel)
36106     {
36107         if(arguments.length > 1){
36108             for(var i = 0, len = arguments.length; i < len; i++) {
36109                 this.add(arguments[i]);
36110             }
36111             return null;
36112         }
36113         
36114         // if we have not been rendered yet, then we can not really do much of this..
36115         if (!this.bodyEl) {
36116             this.unrendered_panels.push(panel);
36117             return panel;
36118         }
36119         
36120         
36121         
36122         
36123         if(this.hasPanel(panel)){
36124             this.showPanel(panel);
36125             return panel;
36126         }
36127         panel.setRegion(this);
36128         this.panels.add(panel);
36129        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36130             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36131             // and hide them... ???
36132             this.bodyEl.dom.appendChild(panel.getEl().dom);
36133             if(panel.background !== true){
36134                 this.setActivePanel(panel);
36135             }
36136             this.fireEvent("paneladded", this, panel);
36137             return panel;
36138         }
36139         */
36140         if(!this.tabs){
36141             this.initTabs();
36142         }else{
36143             this.initPanelAsTab(panel);
36144         }
36145         
36146         
36147         if(panel.background !== true){
36148             this.tabs.activate(panel.getEl().id);
36149         }
36150         this.fireEvent("paneladded", this, panel);
36151         return panel;
36152     },
36153
36154     /**
36155      * Hides the tab for the specified panel.
36156      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36157      */
36158     hidePanel : function(panel){
36159         if(this.tabs && (panel = this.getPanel(panel))){
36160             this.tabs.hideTab(panel.getEl().id);
36161         }
36162     },
36163
36164     /**
36165      * Unhides the tab for a previously hidden panel.
36166      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36167      */
36168     unhidePanel : function(panel){
36169         if(this.tabs && (panel = this.getPanel(panel))){
36170             this.tabs.unhideTab(panel.getEl().id);
36171         }
36172     },
36173
36174     clearPanels : function(){
36175         while(this.panels.getCount() > 0){
36176              this.remove(this.panels.first());
36177         }
36178     },
36179
36180     /**
36181      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36182      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36183      * @param {Boolean} preservePanel Overrides the config preservePanel option
36184      * @return {Roo.ContentPanel} The panel that was removed
36185      */
36186     remove : function(panel, preservePanel)
36187     {
36188         panel = this.getPanel(panel);
36189         if(!panel){
36190             return null;
36191         }
36192         var e = {};
36193         this.fireEvent("beforeremove", this, panel, e);
36194         if(e.cancel === true){
36195             return null;
36196         }
36197         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36198         var panelId = panel.getId();
36199         this.panels.removeKey(panelId);
36200         if(preservePanel){
36201             document.body.appendChild(panel.getEl().dom);
36202         }
36203         if(this.tabs){
36204             this.tabs.removeTab(panel.getEl().id);
36205         }else if (!preservePanel){
36206             this.bodyEl.dom.removeChild(panel.getEl().dom);
36207         }
36208         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36209             var p = this.panels.first();
36210             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36211             tempEl.appendChild(p.getEl().dom);
36212             this.bodyEl.update("");
36213             this.bodyEl.dom.appendChild(p.getEl().dom);
36214             tempEl = null;
36215             this.updateTitle(p.getTitle());
36216             this.tabs = null;
36217             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36218             this.setActivePanel(p);
36219         }
36220         panel.setRegion(null);
36221         if(this.activePanel == panel){
36222             this.activePanel = null;
36223         }
36224         if(this.config.autoDestroy !== false && preservePanel !== true){
36225             try{panel.destroy();}catch(e){}
36226         }
36227         this.fireEvent("panelremoved", this, panel);
36228         return panel;
36229     },
36230
36231     /**
36232      * Returns the TabPanel component used by this region
36233      * @return {Roo.TabPanel}
36234      */
36235     getTabs : function(){
36236         return this.tabs;
36237     },
36238
36239     createTool : function(parentEl, className){
36240         var btn = Roo.DomHelper.append(parentEl, {
36241             tag: "div",
36242             cls: "x-layout-tools-button",
36243             children: [ {
36244                 tag: "div",
36245                 cls: "roo-layout-tools-button-inner " + className,
36246                 html: "&#160;"
36247             }]
36248         }, true);
36249         btn.addClassOnOver("roo-layout-tools-button-over");
36250         return btn;
36251     }
36252 });/*
36253  * Based on:
36254  * Ext JS Library 1.1.1
36255  * Copyright(c) 2006-2007, Ext JS, LLC.
36256  *
36257  * Originally Released Under LGPL - original licence link has changed is not relivant.
36258  *
36259  * Fork - LGPL
36260  * <script type="text/javascript">
36261  */
36262  
36263
36264
36265 /**
36266  * @class Roo.SplitLayoutRegion
36267  * @extends Roo.LayoutRegion
36268  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36269  */
36270 Roo.bootstrap.layout.Split = function(config){
36271     this.cursor = config.cursor;
36272     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36273 };
36274
36275 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36276 {
36277     splitTip : "Drag to resize.",
36278     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36279     useSplitTips : false,
36280
36281     applyConfig : function(config){
36282         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36283     },
36284     
36285     onRender : function(ctr,pos) {
36286         
36287         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36288         if(!this.config.split){
36289             return;
36290         }
36291         if(!this.split){
36292             
36293             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36294                             tag: "div",
36295                             id: this.el.id + "-split",
36296                             cls: "roo-layout-split roo-layout-split-"+this.position,
36297                             html: "&#160;"
36298             });
36299             /** The SplitBar for this region 
36300             * @type Roo.SplitBar */
36301             // does not exist yet...
36302             Roo.log([this.position, this.orientation]);
36303             
36304             this.split = new Roo.bootstrap.SplitBar({
36305                 dragElement : splitEl,
36306                 resizingElement: this.el,
36307                 orientation : this.orientation
36308             });
36309             
36310             this.split.on("moved", this.onSplitMove, this);
36311             this.split.useShim = this.config.useShim === true;
36312             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36313             if(this.useSplitTips){
36314                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36315             }
36316             //if(config.collapsible){
36317             //    this.split.el.on("dblclick", this.collapse,  this);
36318             //}
36319         }
36320         if(typeof this.config.minSize != "undefined"){
36321             this.split.minSize = this.config.minSize;
36322         }
36323         if(typeof this.config.maxSize != "undefined"){
36324             this.split.maxSize = this.config.maxSize;
36325         }
36326         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36327             this.hideSplitter();
36328         }
36329         
36330     },
36331
36332     getHMaxSize : function(){
36333          var cmax = this.config.maxSize || 10000;
36334          var center = this.mgr.getRegion("center");
36335          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36336     },
36337
36338     getVMaxSize : function(){
36339          var cmax = this.config.maxSize || 10000;
36340          var center = this.mgr.getRegion("center");
36341          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36342     },
36343
36344     onSplitMove : function(split, newSize){
36345         this.fireEvent("resized", this, newSize);
36346     },
36347     
36348     /** 
36349      * Returns the {@link Roo.SplitBar} for this region.
36350      * @return {Roo.SplitBar}
36351      */
36352     getSplitBar : function(){
36353         return this.split;
36354     },
36355     
36356     hide : function(){
36357         this.hideSplitter();
36358         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36359     },
36360
36361     hideSplitter : function(){
36362         if(this.split){
36363             this.split.el.setLocation(-2000,-2000);
36364             this.split.el.hide();
36365         }
36366     },
36367
36368     show : function(){
36369         if(this.split){
36370             this.split.el.show();
36371         }
36372         Roo.bootstrap.layout.Split.superclass.show.call(this);
36373     },
36374     
36375     beforeSlide: function(){
36376         if(Roo.isGecko){// firefox overflow auto bug workaround
36377             this.bodyEl.clip();
36378             if(this.tabs) {
36379                 this.tabs.bodyEl.clip();
36380             }
36381             if(this.activePanel){
36382                 this.activePanel.getEl().clip();
36383                 
36384                 if(this.activePanel.beforeSlide){
36385                     this.activePanel.beforeSlide();
36386                 }
36387             }
36388         }
36389     },
36390     
36391     afterSlide : function(){
36392         if(Roo.isGecko){// firefox overflow auto bug workaround
36393             this.bodyEl.unclip();
36394             if(this.tabs) {
36395                 this.tabs.bodyEl.unclip();
36396             }
36397             if(this.activePanel){
36398                 this.activePanel.getEl().unclip();
36399                 if(this.activePanel.afterSlide){
36400                     this.activePanel.afterSlide();
36401                 }
36402             }
36403         }
36404     },
36405
36406     initAutoHide : function(){
36407         if(this.autoHide !== false){
36408             if(!this.autoHideHd){
36409                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36410                 this.autoHideHd = {
36411                     "mouseout": function(e){
36412                         if(!e.within(this.el, true)){
36413                             st.delay(500);
36414                         }
36415                     },
36416                     "mouseover" : function(e){
36417                         st.cancel();
36418                     },
36419                     scope : this
36420                 };
36421             }
36422             this.el.on(this.autoHideHd);
36423         }
36424     },
36425
36426     clearAutoHide : function(){
36427         if(this.autoHide !== false){
36428             this.el.un("mouseout", this.autoHideHd.mouseout);
36429             this.el.un("mouseover", this.autoHideHd.mouseover);
36430         }
36431     },
36432
36433     clearMonitor : function(){
36434         Roo.get(document).un("click", this.slideInIf, this);
36435     },
36436
36437     // these names are backwards but not changed for compat
36438     slideOut : function(){
36439         if(this.isSlid || this.el.hasActiveFx()){
36440             return;
36441         }
36442         this.isSlid = true;
36443         if(this.collapseBtn){
36444             this.collapseBtn.hide();
36445         }
36446         this.closeBtnState = this.closeBtn.getStyle('display');
36447         this.closeBtn.hide();
36448         if(this.stickBtn){
36449             this.stickBtn.show();
36450         }
36451         this.el.show();
36452         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36453         this.beforeSlide();
36454         this.el.setStyle("z-index", 10001);
36455         this.el.slideIn(this.getSlideAnchor(), {
36456             callback: function(){
36457                 this.afterSlide();
36458                 this.initAutoHide();
36459                 Roo.get(document).on("click", this.slideInIf, this);
36460                 this.fireEvent("slideshow", this);
36461             },
36462             scope: this,
36463             block: true
36464         });
36465     },
36466
36467     afterSlideIn : function(){
36468         this.clearAutoHide();
36469         this.isSlid = false;
36470         this.clearMonitor();
36471         this.el.setStyle("z-index", "");
36472         if(this.collapseBtn){
36473             this.collapseBtn.show();
36474         }
36475         this.closeBtn.setStyle('display', this.closeBtnState);
36476         if(this.stickBtn){
36477             this.stickBtn.hide();
36478         }
36479         this.fireEvent("slidehide", this);
36480     },
36481
36482     slideIn : function(cb){
36483         if(!this.isSlid || this.el.hasActiveFx()){
36484             Roo.callback(cb);
36485             return;
36486         }
36487         this.isSlid = false;
36488         this.beforeSlide();
36489         this.el.slideOut(this.getSlideAnchor(), {
36490             callback: function(){
36491                 this.el.setLeftTop(-10000, -10000);
36492                 this.afterSlide();
36493                 this.afterSlideIn();
36494                 Roo.callback(cb);
36495             },
36496             scope: this,
36497             block: true
36498         });
36499     },
36500     
36501     slideInIf : function(e){
36502         if(!e.within(this.el)){
36503             this.slideIn();
36504         }
36505     },
36506
36507     animateCollapse : function(){
36508         this.beforeSlide();
36509         this.el.setStyle("z-index", 20000);
36510         var anchor = this.getSlideAnchor();
36511         this.el.slideOut(anchor, {
36512             callback : function(){
36513                 this.el.setStyle("z-index", "");
36514                 this.collapsedEl.slideIn(anchor, {duration:.3});
36515                 this.afterSlide();
36516                 this.el.setLocation(-10000,-10000);
36517                 this.el.hide();
36518                 this.fireEvent("collapsed", this);
36519             },
36520             scope: this,
36521             block: true
36522         });
36523     },
36524
36525     animateExpand : function(){
36526         this.beforeSlide();
36527         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36528         this.el.setStyle("z-index", 20000);
36529         this.collapsedEl.hide({
36530             duration:.1
36531         });
36532         this.el.slideIn(this.getSlideAnchor(), {
36533             callback : function(){
36534                 this.el.setStyle("z-index", "");
36535                 this.afterSlide();
36536                 if(this.split){
36537                     this.split.el.show();
36538                 }
36539                 this.fireEvent("invalidated", this);
36540                 this.fireEvent("expanded", this);
36541             },
36542             scope: this,
36543             block: true
36544         });
36545     },
36546
36547     anchors : {
36548         "west" : "left",
36549         "east" : "right",
36550         "north" : "top",
36551         "south" : "bottom"
36552     },
36553
36554     sanchors : {
36555         "west" : "l",
36556         "east" : "r",
36557         "north" : "t",
36558         "south" : "b"
36559     },
36560
36561     canchors : {
36562         "west" : "tl-tr",
36563         "east" : "tr-tl",
36564         "north" : "tl-bl",
36565         "south" : "bl-tl"
36566     },
36567
36568     getAnchor : function(){
36569         return this.anchors[this.position];
36570     },
36571
36572     getCollapseAnchor : function(){
36573         return this.canchors[this.position];
36574     },
36575
36576     getSlideAnchor : function(){
36577         return this.sanchors[this.position];
36578     },
36579
36580     getAlignAdj : function(){
36581         var cm = this.cmargins;
36582         switch(this.position){
36583             case "west":
36584                 return [0, 0];
36585             break;
36586             case "east":
36587                 return [0, 0];
36588             break;
36589             case "north":
36590                 return [0, 0];
36591             break;
36592             case "south":
36593                 return [0, 0];
36594             break;
36595         }
36596     },
36597
36598     getExpandAdj : function(){
36599         var c = this.collapsedEl, cm = this.cmargins;
36600         switch(this.position){
36601             case "west":
36602                 return [-(cm.right+c.getWidth()+cm.left), 0];
36603             break;
36604             case "east":
36605                 return [cm.right+c.getWidth()+cm.left, 0];
36606             break;
36607             case "north":
36608                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36609             break;
36610             case "south":
36611                 return [0, cm.top+cm.bottom+c.getHeight()];
36612             break;
36613         }
36614     }
36615 });/*
36616  * Based on:
36617  * Ext JS Library 1.1.1
36618  * Copyright(c) 2006-2007, Ext JS, LLC.
36619  *
36620  * Originally Released Under LGPL - original licence link has changed is not relivant.
36621  *
36622  * Fork - LGPL
36623  * <script type="text/javascript">
36624  */
36625 /*
36626  * These classes are private internal classes
36627  */
36628 Roo.bootstrap.layout.Center = function(config){
36629     config.region = "center";
36630     Roo.bootstrap.layout.Region.call(this, config);
36631     this.visible = true;
36632     this.minWidth = config.minWidth || 20;
36633     this.minHeight = config.minHeight || 20;
36634 };
36635
36636 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36637     hide : function(){
36638         // center panel can't be hidden
36639     },
36640     
36641     show : function(){
36642         // center panel can't be hidden
36643     },
36644     
36645     getMinWidth: function(){
36646         return this.minWidth;
36647     },
36648     
36649     getMinHeight: function(){
36650         return this.minHeight;
36651     }
36652 });
36653
36654
36655
36656
36657  
36658
36659
36660
36661
36662
36663 Roo.bootstrap.layout.North = function(config)
36664 {
36665     config.region = 'north';
36666     config.cursor = 'n-resize';
36667     
36668     Roo.bootstrap.layout.Split.call(this, config);
36669     
36670     
36671     if(this.split){
36672         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36673         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36674         this.split.el.addClass("roo-layout-split-v");
36675     }
36676     var size = config.initialSize || config.height;
36677     if(typeof size != "undefined"){
36678         this.el.setHeight(size);
36679     }
36680 };
36681 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36682 {
36683     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36684     
36685     
36686     
36687     getBox : function(){
36688         if(this.collapsed){
36689             return this.collapsedEl.getBox();
36690         }
36691         var box = this.el.getBox();
36692         if(this.split){
36693             box.height += this.split.el.getHeight();
36694         }
36695         return box;
36696     },
36697     
36698     updateBox : function(box){
36699         if(this.split && !this.collapsed){
36700             box.height -= this.split.el.getHeight();
36701             this.split.el.setLeft(box.x);
36702             this.split.el.setTop(box.y+box.height);
36703             this.split.el.setWidth(box.width);
36704         }
36705         if(this.collapsed){
36706             this.updateBody(box.width, null);
36707         }
36708         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36709     }
36710 });
36711
36712
36713
36714
36715
36716 Roo.bootstrap.layout.South = function(config){
36717     config.region = 'south';
36718     config.cursor = 's-resize';
36719     Roo.bootstrap.layout.Split.call(this, config);
36720     if(this.split){
36721         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36722         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36723         this.split.el.addClass("roo-layout-split-v");
36724     }
36725     var size = config.initialSize || config.height;
36726     if(typeof size != "undefined"){
36727         this.el.setHeight(size);
36728     }
36729 };
36730
36731 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36732     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36733     getBox : function(){
36734         if(this.collapsed){
36735             return this.collapsedEl.getBox();
36736         }
36737         var box = this.el.getBox();
36738         if(this.split){
36739             var sh = this.split.el.getHeight();
36740             box.height += sh;
36741             box.y -= sh;
36742         }
36743         return box;
36744     },
36745     
36746     updateBox : function(box){
36747         if(this.split && !this.collapsed){
36748             var sh = this.split.el.getHeight();
36749             box.height -= sh;
36750             box.y += sh;
36751             this.split.el.setLeft(box.x);
36752             this.split.el.setTop(box.y-sh);
36753             this.split.el.setWidth(box.width);
36754         }
36755         if(this.collapsed){
36756             this.updateBody(box.width, null);
36757         }
36758         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36759     }
36760 });
36761
36762 Roo.bootstrap.layout.East = function(config){
36763     config.region = "east";
36764     config.cursor = "e-resize";
36765     Roo.bootstrap.layout.Split.call(this, config);
36766     if(this.split){
36767         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36768         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36769         this.split.el.addClass("roo-layout-split-h");
36770     }
36771     var size = config.initialSize || config.width;
36772     if(typeof size != "undefined"){
36773         this.el.setWidth(size);
36774     }
36775 };
36776 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36777     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36778     getBox : function(){
36779         if(this.collapsed){
36780             return this.collapsedEl.getBox();
36781         }
36782         var box = this.el.getBox();
36783         if(this.split){
36784             var sw = this.split.el.getWidth();
36785             box.width += sw;
36786             box.x -= sw;
36787         }
36788         return box;
36789     },
36790
36791     updateBox : function(box){
36792         if(this.split && !this.collapsed){
36793             var sw = this.split.el.getWidth();
36794             box.width -= sw;
36795             this.split.el.setLeft(box.x);
36796             this.split.el.setTop(box.y);
36797             this.split.el.setHeight(box.height);
36798             box.x += sw;
36799         }
36800         if(this.collapsed){
36801             this.updateBody(null, box.height);
36802         }
36803         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36804     }
36805 });
36806
36807 Roo.bootstrap.layout.West = function(config){
36808     config.region = "west";
36809     config.cursor = "w-resize";
36810     
36811     Roo.bootstrap.layout.Split.call(this, config);
36812     if(this.split){
36813         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36814         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36815         this.split.el.addClass("roo-layout-split-h");
36816     }
36817     
36818 };
36819 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36820     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36821     
36822     onRender: function(ctr, pos)
36823     {
36824         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36825         var size = this.config.initialSize || this.config.width;
36826         if(typeof size != "undefined"){
36827             this.el.setWidth(size);
36828         }
36829     },
36830     
36831     getBox : function(){
36832         if(this.collapsed){
36833             return this.collapsedEl.getBox();
36834         }
36835         var box = this.el.getBox();
36836         if(this.split){
36837             box.width += this.split.el.getWidth();
36838         }
36839         return box;
36840     },
36841     
36842     updateBox : function(box){
36843         if(this.split && !this.collapsed){
36844             var sw = this.split.el.getWidth();
36845             box.width -= sw;
36846             this.split.el.setLeft(box.x+box.width);
36847             this.split.el.setTop(box.y);
36848             this.split.el.setHeight(box.height);
36849         }
36850         if(this.collapsed){
36851             this.updateBody(null, box.height);
36852         }
36853         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36854     }
36855 });
36856 Roo.namespace("Roo.bootstrap.panel");/*
36857  * Based on:
36858  * Ext JS Library 1.1.1
36859  * Copyright(c) 2006-2007, Ext JS, LLC.
36860  *
36861  * Originally Released Under LGPL - original licence link has changed is not relivant.
36862  *
36863  * Fork - LGPL
36864  * <script type="text/javascript">
36865  */
36866 /**
36867  * @class Roo.ContentPanel
36868  * @extends Roo.util.Observable
36869  * A basic ContentPanel element.
36870  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36871  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36872  * @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
36873  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36874  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36875  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36876  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36877  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36878  * @cfg {String} title          The title for this panel
36879  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36880  * @cfg {String} url            Calls {@link #setUrl} with this value
36881  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36882  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36883  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36884  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36885  * @cfg {Boolean} badges render the badges
36886
36887  * @constructor
36888  * Create a new ContentPanel.
36889  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36890  * @param {String/Object} config A string to set only the title or a config object
36891  * @param {String} content (optional) Set the HTML content for this panel
36892  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36893  */
36894 Roo.bootstrap.panel.Content = function( config){
36895     
36896     this.tpl = config.tpl || false;
36897     
36898     var el = config.el;
36899     var content = config.content;
36900
36901     if(config.autoCreate){ // xtype is available if this is called from factory
36902         el = Roo.id();
36903     }
36904     this.el = Roo.get(el);
36905     if(!this.el && config && config.autoCreate){
36906         if(typeof config.autoCreate == "object"){
36907             if(!config.autoCreate.id){
36908                 config.autoCreate.id = config.id||el;
36909             }
36910             this.el = Roo.DomHelper.append(document.body,
36911                         config.autoCreate, true);
36912         }else{
36913             var elcfg =  {   tag: "div",
36914                             cls: "roo-layout-inactive-content",
36915                             id: config.id||el
36916                             };
36917             if (config.html) {
36918                 elcfg.html = config.html;
36919                 
36920             }
36921                         
36922             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36923         }
36924     } 
36925     this.closable = false;
36926     this.loaded = false;
36927     this.active = false;
36928    
36929       
36930     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36931         
36932         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36933         
36934         this.wrapEl = this.el; //this.el.wrap();
36935         var ti = [];
36936         if (config.toolbar.items) {
36937             ti = config.toolbar.items ;
36938             delete config.toolbar.items ;
36939         }
36940         
36941         var nitems = [];
36942         this.toolbar.render(this.wrapEl, 'before');
36943         for(var i =0;i < ti.length;i++) {
36944           //  Roo.log(['add child', items[i]]);
36945             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36946         }
36947         this.toolbar.items = nitems;
36948         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36949         delete config.toolbar;
36950         
36951     }
36952     /*
36953     // xtype created footer. - not sure if will work as we normally have to render first..
36954     if (this.footer && !this.footer.el && this.footer.xtype) {
36955         if (!this.wrapEl) {
36956             this.wrapEl = this.el.wrap();
36957         }
36958     
36959         this.footer.container = this.wrapEl.createChild();
36960          
36961         this.footer = Roo.factory(this.footer, Roo);
36962         
36963     }
36964     */
36965     
36966      if(typeof config == "string"){
36967         this.title = config;
36968     }else{
36969         Roo.apply(this, config);
36970     }
36971     
36972     if(this.resizeEl){
36973         this.resizeEl = Roo.get(this.resizeEl, true);
36974     }else{
36975         this.resizeEl = this.el;
36976     }
36977     // handle view.xtype
36978     
36979  
36980     
36981     
36982     this.addEvents({
36983         /**
36984          * @event activate
36985          * Fires when this panel is activated. 
36986          * @param {Roo.ContentPanel} this
36987          */
36988         "activate" : true,
36989         /**
36990          * @event deactivate
36991          * Fires when this panel is activated. 
36992          * @param {Roo.ContentPanel} this
36993          */
36994         "deactivate" : true,
36995
36996         /**
36997          * @event resize
36998          * Fires when this panel is resized if fitToFrame is true.
36999          * @param {Roo.ContentPanel} this
37000          * @param {Number} width The width after any component adjustments
37001          * @param {Number} height The height after any component adjustments
37002          */
37003         "resize" : true,
37004         
37005          /**
37006          * @event render
37007          * Fires when this tab is created
37008          * @param {Roo.ContentPanel} this
37009          */
37010         "render" : true
37011         
37012         
37013         
37014     });
37015     
37016
37017     
37018     
37019     if(this.autoScroll){
37020         this.resizeEl.setStyle("overflow", "auto");
37021     } else {
37022         // fix randome scrolling
37023         //this.el.on('scroll', function() {
37024         //    Roo.log('fix random scolling');
37025         //    this.scrollTo('top',0); 
37026         //});
37027     }
37028     content = content || this.content;
37029     if(content){
37030         this.setContent(content);
37031     }
37032     if(config && config.url){
37033         this.setUrl(this.url, this.params, this.loadOnce);
37034     }
37035     
37036     
37037     
37038     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37039     
37040     if (this.view && typeof(this.view.xtype) != 'undefined') {
37041         this.view.el = this.el.appendChild(document.createElement("div"));
37042         this.view = Roo.factory(this.view); 
37043         this.view.render  &&  this.view.render(false, '');  
37044     }
37045     
37046     
37047     this.fireEvent('render', this);
37048 };
37049
37050 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37051     
37052     tabTip : '',
37053     
37054     setRegion : function(region){
37055         this.region = region;
37056         this.setActiveClass(region && !this.background);
37057     },
37058     
37059     
37060     setActiveClass: function(state)
37061     {
37062         if(state){
37063            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37064            this.el.setStyle('position','relative');
37065         }else{
37066            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37067            this.el.setStyle('position', 'absolute');
37068         } 
37069     },
37070     
37071     /**
37072      * Returns the toolbar for this Panel if one was configured. 
37073      * @return {Roo.Toolbar} 
37074      */
37075     getToolbar : function(){
37076         return this.toolbar;
37077     },
37078     
37079     setActiveState : function(active)
37080     {
37081         this.active = active;
37082         this.setActiveClass(active);
37083         if(!active){
37084             if(this.fireEvent("deactivate", this) === false){
37085                 return false;
37086             }
37087             return true;
37088         }
37089         this.fireEvent("activate", this);
37090         return true;
37091     },
37092     /**
37093      * Updates this panel's element
37094      * @param {String} content The new content
37095      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37096     */
37097     setContent : function(content, loadScripts){
37098         this.el.update(content, loadScripts);
37099     },
37100
37101     ignoreResize : function(w, h){
37102         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37103             return true;
37104         }else{
37105             this.lastSize = {width: w, height: h};
37106             return false;
37107         }
37108     },
37109     /**
37110      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37111      * @return {Roo.UpdateManager} The UpdateManager
37112      */
37113     getUpdateManager : function(){
37114         return this.el.getUpdateManager();
37115     },
37116      /**
37117      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37118      * @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:
37119 <pre><code>
37120 panel.load({
37121     url: "your-url.php",
37122     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37123     callback: yourFunction,
37124     scope: yourObject, //(optional scope)
37125     discardUrl: false,
37126     nocache: false,
37127     text: "Loading...",
37128     timeout: 30,
37129     scripts: false
37130 });
37131 </code></pre>
37132      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37133      * 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.
37134      * @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}
37135      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37136      * @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.
37137      * @return {Roo.ContentPanel} this
37138      */
37139     load : function(){
37140         var um = this.el.getUpdateManager();
37141         um.update.apply(um, arguments);
37142         return this;
37143     },
37144
37145
37146     /**
37147      * 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.
37148      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37149      * @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)
37150      * @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)
37151      * @return {Roo.UpdateManager} The UpdateManager
37152      */
37153     setUrl : function(url, params, loadOnce){
37154         if(this.refreshDelegate){
37155             this.removeListener("activate", this.refreshDelegate);
37156         }
37157         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37158         this.on("activate", this.refreshDelegate);
37159         return this.el.getUpdateManager();
37160     },
37161     
37162     _handleRefresh : function(url, params, loadOnce){
37163         if(!loadOnce || !this.loaded){
37164             var updater = this.el.getUpdateManager();
37165             updater.update(url, params, this._setLoaded.createDelegate(this));
37166         }
37167     },
37168     
37169     _setLoaded : function(){
37170         this.loaded = true;
37171     }, 
37172     
37173     /**
37174      * Returns this panel's id
37175      * @return {String} 
37176      */
37177     getId : function(){
37178         return this.el.id;
37179     },
37180     
37181     /** 
37182      * Returns this panel's element - used by regiosn to add.
37183      * @return {Roo.Element} 
37184      */
37185     getEl : function(){
37186         return this.wrapEl || this.el;
37187     },
37188     
37189    
37190     
37191     adjustForComponents : function(width, height)
37192     {
37193         //Roo.log('adjustForComponents ');
37194         if(this.resizeEl != this.el){
37195             width -= this.el.getFrameWidth('lr');
37196             height -= this.el.getFrameWidth('tb');
37197         }
37198         if(this.toolbar){
37199             var te = this.toolbar.getEl();
37200             te.setWidth(width);
37201             height -= te.getHeight();
37202         }
37203         if(this.footer){
37204             var te = this.footer.getEl();
37205             te.setWidth(width);
37206             height -= te.getHeight();
37207         }
37208         
37209         
37210         if(this.adjustments){
37211             width += this.adjustments[0];
37212             height += this.adjustments[1];
37213         }
37214         return {"width": width, "height": height};
37215     },
37216     
37217     setSize : function(width, height){
37218         if(this.fitToFrame && !this.ignoreResize(width, height)){
37219             if(this.fitContainer && this.resizeEl != this.el){
37220                 this.el.setSize(width, height);
37221             }
37222             var size = this.adjustForComponents(width, height);
37223             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37224             this.fireEvent('resize', this, size.width, size.height);
37225         }
37226     },
37227     
37228     /**
37229      * Returns this panel's title
37230      * @return {String} 
37231      */
37232     getTitle : function(){
37233         
37234         if (typeof(this.title) != 'object') {
37235             return this.title;
37236         }
37237         
37238         var t = '';
37239         for (var k in this.title) {
37240             if (!this.title.hasOwnProperty(k)) {
37241                 continue;
37242             }
37243             
37244             if (k.indexOf('-') >= 0) {
37245                 var s = k.split('-');
37246                 for (var i = 0; i<s.length; i++) {
37247                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37248                 }
37249             } else {
37250                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37251             }
37252         }
37253         return t;
37254     },
37255     
37256     /**
37257      * Set this panel's title
37258      * @param {String} title
37259      */
37260     setTitle : function(title){
37261         this.title = title;
37262         if(this.region){
37263             this.region.updatePanelTitle(this, title);
37264         }
37265     },
37266     
37267     /**
37268      * Returns true is this panel was configured to be closable
37269      * @return {Boolean} 
37270      */
37271     isClosable : function(){
37272         return this.closable;
37273     },
37274     
37275     beforeSlide : function(){
37276         this.el.clip();
37277         this.resizeEl.clip();
37278     },
37279     
37280     afterSlide : function(){
37281         this.el.unclip();
37282         this.resizeEl.unclip();
37283     },
37284     
37285     /**
37286      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37287      *   Will fail silently if the {@link #setUrl} method has not been called.
37288      *   This does not activate the panel, just updates its content.
37289      */
37290     refresh : function(){
37291         if(this.refreshDelegate){
37292            this.loaded = false;
37293            this.refreshDelegate();
37294         }
37295     },
37296     
37297     /**
37298      * Destroys this panel
37299      */
37300     destroy : function(){
37301         this.el.removeAllListeners();
37302         var tempEl = document.createElement("span");
37303         tempEl.appendChild(this.el.dom);
37304         tempEl.innerHTML = "";
37305         this.el.remove();
37306         this.el = null;
37307     },
37308     
37309     /**
37310      * form - if the content panel contains a form - this is a reference to it.
37311      * @type {Roo.form.Form}
37312      */
37313     form : false,
37314     /**
37315      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37316      *    This contains a reference to it.
37317      * @type {Roo.View}
37318      */
37319     view : false,
37320     
37321       /**
37322      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37323      * <pre><code>
37324
37325 layout.addxtype({
37326        xtype : 'Form',
37327        items: [ .... ]
37328    }
37329 );
37330
37331 </code></pre>
37332      * @param {Object} cfg Xtype definition of item to add.
37333      */
37334     
37335     
37336     getChildContainer: function () {
37337         return this.getEl();
37338     }
37339     
37340     
37341     /*
37342         var  ret = new Roo.factory(cfg);
37343         return ret;
37344         
37345         
37346         // add form..
37347         if (cfg.xtype.match(/^Form$/)) {
37348             
37349             var el;
37350             //if (this.footer) {
37351             //    el = this.footer.container.insertSibling(false, 'before');
37352             //} else {
37353                 el = this.el.createChild();
37354             //}
37355
37356             this.form = new  Roo.form.Form(cfg);
37357             
37358             
37359             if ( this.form.allItems.length) {
37360                 this.form.render(el.dom);
37361             }
37362             return this.form;
37363         }
37364         // should only have one of theses..
37365         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37366             // views.. should not be just added - used named prop 'view''
37367             
37368             cfg.el = this.el.appendChild(document.createElement("div"));
37369             // factory?
37370             
37371             var ret = new Roo.factory(cfg);
37372              
37373              ret.render && ret.render(false, ''); // render blank..
37374             this.view = ret;
37375             return ret;
37376         }
37377         return false;
37378     }
37379     \*/
37380 });
37381  
37382 /**
37383  * @class Roo.bootstrap.panel.Grid
37384  * @extends Roo.bootstrap.panel.Content
37385  * @constructor
37386  * Create a new GridPanel.
37387  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37388  * @param {Object} config A the config object
37389   
37390  */
37391
37392
37393
37394 Roo.bootstrap.panel.Grid = function(config)
37395 {
37396     
37397       
37398     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37399         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37400
37401     config.el = this.wrapper;
37402     //this.el = this.wrapper;
37403     
37404       if (config.container) {
37405         // ctor'ed from a Border/panel.grid
37406         
37407         
37408         this.wrapper.setStyle("overflow", "hidden");
37409         this.wrapper.addClass('roo-grid-container');
37410
37411     }
37412     
37413     
37414     if(config.toolbar){
37415         var tool_el = this.wrapper.createChild();    
37416         this.toolbar = Roo.factory(config.toolbar);
37417         var ti = [];
37418         if (config.toolbar.items) {
37419             ti = config.toolbar.items ;
37420             delete config.toolbar.items ;
37421         }
37422         
37423         var nitems = [];
37424         this.toolbar.render(tool_el);
37425         for(var i =0;i < ti.length;i++) {
37426           //  Roo.log(['add child', items[i]]);
37427             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37428         }
37429         this.toolbar.items = nitems;
37430         
37431         delete config.toolbar;
37432     }
37433     
37434     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37435     config.grid.scrollBody = true;;
37436     config.grid.monitorWindowResize = false; // turn off autosizing
37437     config.grid.autoHeight = false;
37438     config.grid.autoWidth = false;
37439     
37440     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37441     
37442     if (config.background) {
37443         // render grid on panel activation (if panel background)
37444         this.on('activate', function(gp) {
37445             if (!gp.grid.rendered) {
37446                 gp.grid.render(this.wrapper);
37447                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37448             }
37449         });
37450             
37451     } else {
37452         this.grid.render(this.wrapper);
37453         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37454
37455     }
37456     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37457     // ??? needed ??? config.el = this.wrapper;
37458     
37459     
37460     
37461   
37462     // xtype created footer. - not sure if will work as we normally have to render first..
37463     if (this.footer && !this.footer.el && this.footer.xtype) {
37464         
37465         var ctr = this.grid.getView().getFooterPanel(true);
37466         this.footer.dataSource = this.grid.dataSource;
37467         this.footer = Roo.factory(this.footer, Roo);
37468         this.footer.render(ctr);
37469         
37470     }
37471     
37472     
37473     
37474     
37475      
37476 };
37477
37478 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37479     getId : function(){
37480         return this.grid.id;
37481     },
37482     
37483     /**
37484      * Returns the grid for this panel
37485      * @return {Roo.bootstrap.Table} 
37486      */
37487     getGrid : function(){
37488         return this.grid;    
37489     },
37490     
37491     setSize : function(width, height){
37492         if(!this.ignoreResize(width, height)){
37493             var grid = this.grid;
37494             var size = this.adjustForComponents(width, height);
37495             var gridel = grid.getGridEl();
37496             gridel.setSize(size.width, size.height);
37497             /*
37498             var thd = grid.getGridEl().select('thead',true).first();
37499             var tbd = grid.getGridEl().select('tbody', true).first();
37500             if (tbd) {
37501                 tbd.setSize(width, height - thd.getHeight());
37502             }
37503             */
37504             grid.autoSize();
37505         }
37506     },
37507      
37508     
37509     
37510     beforeSlide : function(){
37511         this.grid.getView().scroller.clip();
37512     },
37513     
37514     afterSlide : function(){
37515         this.grid.getView().scroller.unclip();
37516     },
37517     
37518     destroy : function(){
37519         this.grid.destroy();
37520         delete this.grid;
37521         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37522     }
37523 });
37524
37525 /**
37526  * @class Roo.bootstrap.panel.Nest
37527  * @extends Roo.bootstrap.panel.Content
37528  * @constructor
37529  * Create a new Panel, that can contain a layout.Border.
37530  * 
37531  * 
37532  * @param {Roo.BorderLayout} layout The layout for this panel
37533  * @param {String/Object} config A string to set only the title or a config object
37534  */
37535 Roo.bootstrap.panel.Nest = function(config)
37536 {
37537     // construct with only one argument..
37538     /* FIXME - implement nicer consturctors
37539     if (layout.layout) {
37540         config = layout;
37541         layout = config.layout;
37542         delete config.layout;
37543     }
37544     if (layout.xtype && !layout.getEl) {
37545         // then layout needs constructing..
37546         layout = Roo.factory(layout, Roo);
37547     }
37548     */
37549     
37550     config.el =  config.layout.getEl();
37551     
37552     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37553     
37554     config.layout.monitorWindowResize = false; // turn off autosizing
37555     this.layout = config.layout;
37556     this.layout.getEl().addClass("roo-layout-nested-layout");
37557     
37558     
37559     
37560     
37561 };
37562
37563 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37564
37565     setSize : function(width, height){
37566         if(!this.ignoreResize(width, height)){
37567             var size = this.adjustForComponents(width, height);
37568             var el = this.layout.getEl();
37569             if (size.height < 1) {
37570                 el.setWidth(size.width);   
37571             } else {
37572                 el.setSize(size.width, size.height);
37573             }
37574             var touch = el.dom.offsetWidth;
37575             this.layout.layout();
37576             // ie requires a double layout on the first pass
37577             if(Roo.isIE && !this.initialized){
37578                 this.initialized = true;
37579                 this.layout.layout();
37580             }
37581         }
37582     },
37583     
37584     // activate all subpanels if not currently active..
37585     
37586     setActiveState : function(active){
37587         this.active = active;
37588         this.setActiveClass(active);
37589         
37590         if(!active){
37591             this.fireEvent("deactivate", this);
37592             return;
37593         }
37594         
37595         this.fireEvent("activate", this);
37596         // not sure if this should happen before or after..
37597         if (!this.layout) {
37598             return; // should not happen..
37599         }
37600         var reg = false;
37601         for (var r in this.layout.regions) {
37602             reg = this.layout.getRegion(r);
37603             if (reg.getActivePanel()) {
37604                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37605                 reg.setActivePanel(reg.getActivePanel());
37606                 continue;
37607             }
37608             if (!reg.panels.length) {
37609                 continue;
37610             }
37611             reg.showPanel(reg.getPanel(0));
37612         }
37613         
37614         
37615         
37616         
37617     },
37618     
37619     /**
37620      * Returns the nested BorderLayout for this panel
37621      * @return {Roo.BorderLayout} 
37622      */
37623     getLayout : function(){
37624         return this.layout;
37625     },
37626     
37627      /**
37628      * Adds a xtype elements to the layout of the nested panel
37629      * <pre><code>
37630
37631 panel.addxtype({
37632        xtype : 'ContentPanel',
37633        region: 'west',
37634        items: [ .... ]
37635    }
37636 );
37637
37638 panel.addxtype({
37639         xtype : 'NestedLayoutPanel',
37640         region: 'west',
37641         layout: {
37642            center: { },
37643            west: { }   
37644         },
37645         items : [ ... list of content panels or nested layout panels.. ]
37646    }
37647 );
37648 </code></pre>
37649      * @param {Object} cfg Xtype definition of item to add.
37650      */
37651     addxtype : function(cfg) {
37652         return this.layout.addxtype(cfg);
37653     
37654     }
37655 });        /*
37656  * Based on:
37657  * Ext JS Library 1.1.1
37658  * Copyright(c) 2006-2007, Ext JS, LLC.
37659  *
37660  * Originally Released Under LGPL - original licence link has changed is not relivant.
37661  *
37662  * Fork - LGPL
37663  * <script type="text/javascript">
37664  */
37665 /**
37666  * @class Roo.TabPanel
37667  * @extends Roo.util.Observable
37668  * A lightweight tab container.
37669  * <br><br>
37670  * Usage:
37671  * <pre><code>
37672 // basic tabs 1, built from existing content
37673 var tabs = new Roo.TabPanel("tabs1");
37674 tabs.addTab("script", "View Script");
37675 tabs.addTab("markup", "View Markup");
37676 tabs.activate("script");
37677
37678 // more advanced tabs, built from javascript
37679 var jtabs = new Roo.TabPanel("jtabs");
37680 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37681
37682 // set up the UpdateManager
37683 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37684 var updater = tab2.getUpdateManager();
37685 updater.setDefaultUrl("ajax1.htm");
37686 tab2.on('activate', updater.refresh, updater, true);
37687
37688 // Use setUrl for Ajax loading
37689 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37690 tab3.setUrl("ajax2.htm", null, true);
37691
37692 // Disabled tab
37693 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37694 tab4.disable();
37695
37696 jtabs.activate("jtabs-1");
37697  * </code></pre>
37698  * @constructor
37699  * Create a new TabPanel.
37700  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37701  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37702  */
37703 Roo.bootstrap.panel.Tabs = function(config){
37704     /**
37705     * The container element for this TabPanel.
37706     * @type Roo.Element
37707     */
37708     this.el = Roo.get(config.el);
37709     delete config.el;
37710     if(config){
37711         if(typeof config == "boolean"){
37712             this.tabPosition = config ? "bottom" : "top";
37713         }else{
37714             Roo.apply(this, config);
37715         }
37716     }
37717     
37718     if(this.tabPosition == "bottom"){
37719         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37720         this.el.addClass("roo-tabs-bottom");
37721     }
37722     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37723     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37724     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37725     if(Roo.isIE){
37726         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37727     }
37728     if(this.tabPosition != "bottom"){
37729         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37730          * @type Roo.Element
37731          */
37732         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37733         this.el.addClass("roo-tabs-top");
37734     }
37735     this.items = [];
37736
37737     this.bodyEl.setStyle("position", "relative");
37738
37739     this.active = null;
37740     this.activateDelegate = this.activate.createDelegate(this);
37741
37742     this.addEvents({
37743         /**
37744          * @event tabchange
37745          * Fires when the active tab changes
37746          * @param {Roo.TabPanel} this
37747          * @param {Roo.TabPanelItem} activePanel The new active tab
37748          */
37749         "tabchange": true,
37750         /**
37751          * @event beforetabchange
37752          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37753          * @param {Roo.TabPanel} this
37754          * @param {Object} e Set cancel to true on this object to cancel the tab change
37755          * @param {Roo.TabPanelItem} tab The tab being changed to
37756          */
37757         "beforetabchange" : true
37758     });
37759
37760     Roo.EventManager.onWindowResize(this.onResize, this);
37761     this.cpad = this.el.getPadding("lr");
37762     this.hiddenCount = 0;
37763
37764
37765     // toolbar on the tabbar support...
37766     if (this.toolbar) {
37767         alert("no toolbar support yet");
37768         this.toolbar  = false;
37769         /*
37770         var tcfg = this.toolbar;
37771         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37772         this.toolbar = new Roo.Toolbar(tcfg);
37773         if (Roo.isSafari) {
37774             var tbl = tcfg.container.child('table', true);
37775             tbl.setAttribute('width', '100%');
37776         }
37777         */
37778         
37779     }
37780    
37781
37782
37783     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37784 };
37785
37786 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37787     /*
37788      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37789      */
37790     tabPosition : "top",
37791     /*
37792      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37793      */
37794     currentTabWidth : 0,
37795     /*
37796      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37797      */
37798     minTabWidth : 40,
37799     /*
37800      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37801      */
37802     maxTabWidth : 250,
37803     /*
37804      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37805      */
37806     preferredTabWidth : 175,
37807     /*
37808      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37809      */
37810     resizeTabs : false,
37811     /*
37812      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37813      */
37814     monitorResize : true,
37815     /*
37816      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37817      */
37818     toolbar : false,
37819
37820     /**
37821      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37822      * @param {String} id The id of the div to use <b>or create</b>
37823      * @param {String} text The text for the tab
37824      * @param {String} content (optional) Content to put in the TabPanelItem body
37825      * @param {Boolean} closable (optional) True to create a close icon on the tab
37826      * @return {Roo.TabPanelItem} The created TabPanelItem
37827      */
37828     addTab : function(id, text, content, closable, tpl)
37829     {
37830         var item = new Roo.bootstrap.panel.TabItem({
37831             panel: this,
37832             id : id,
37833             text : text,
37834             closable : closable,
37835             tpl : tpl
37836         });
37837         this.addTabItem(item);
37838         if(content){
37839             item.setContent(content);
37840         }
37841         return item;
37842     },
37843
37844     /**
37845      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37846      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37847      * @return {Roo.TabPanelItem}
37848      */
37849     getTab : function(id){
37850         return this.items[id];
37851     },
37852
37853     /**
37854      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37855      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37856      */
37857     hideTab : function(id){
37858         var t = this.items[id];
37859         if(!t.isHidden()){
37860            t.setHidden(true);
37861            this.hiddenCount++;
37862            this.autoSizeTabs();
37863         }
37864     },
37865
37866     /**
37867      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37868      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37869      */
37870     unhideTab : function(id){
37871         var t = this.items[id];
37872         if(t.isHidden()){
37873            t.setHidden(false);
37874            this.hiddenCount--;
37875            this.autoSizeTabs();
37876         }
37877     },
37878
37879     /**
37880      * Adds an existing {@link Roo.TabPanelItem}.
37881      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37882      */
37883     addTabItem : function(item){
37884         this.items[item.id] = item;
37885         this.items.push(item);
37886       //  if(this.resizeTabs){
37887     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37888   //         this.autoSizeTabs();
37889 //        }else{
37890 //            item.autoSize();
37891        // }
37892     },
37893
37894     /**
37895      * Removes a {@link Roo.TabPanelItem}.
37896      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37897      */
37898     removeTab : function(id){
37899         var items = this.items;
37900         var tab = items[id];
37901         if(!tab) { return; }
37902         var index = items.indexOf(tab);
37903         if(this.active == tab && items.length > 1){
37904             var newTab = this.getNextAvailable(index);
37905             if(newTab) {
37906                 newTab.activate();
37907             }
37908         }
37909         this.stripEl.dom.removeChild(tab.pnode.dom);
37910         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37911             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37912         }
37913         items.splice(index, 1);
37914         delete this.items[tab.id];
37915         tab.fireEvent("close", tab);
37916         tab.purgeListeners();
37917         this.autoSizeTabs();
37918     },
37919
37920     getNextAvailable : function(start){
37921         var items = this.items;
37922         var index = start;
37923         // look for a next tab that will slide over to
37924         // replace the one being removed
37925         while(index < items.length){
37926             var item = items[++index];
37927             if(item && !item.isHidden()){
37928                 return item;
37929             }
37930         }
37931         // if one isn't found select the previous tab (on the left)
37932         index = start;
37933         while(index >= 0){
37934             var item = items[--index];
37935             if(item && !item.isHidden()){
37936                 return item;
37937             }
37938         }
37939         return null;
37940     },
37941
37942     /**
37943      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37944      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37945      */
37946     disableTab : function(id){
37947         var tab = this.items[id];
37948         if(tab && this.active != tab){
37949             tab.disable();
37950         }
37951     },
37952
37953     /**
37954      * Enables a {@link Roo.TabPanelItem} that is disabled.
37955      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37956      */
37957     enableTab : function(id){
37958         var tab = this.items[id];
37959         tab.enable();
37960     },
37961
37962     /**
37963      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37964      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37965      * @return {Roo.TabPanelItem} The TabPanelItem.
37966      */
37967     activate : function(id){
37968         var tab = this.items[id];
37969         if(!tab){
37970             return null;
37971         }
37972         if(tab == this.active || tab.disabled){
37973             return tab;
37974         }
37975         var e = {};
37976         this.fireEvent("beforetabchange", this, e, tab);
37977         if(e.cancel !== true && !tab.disabled){
37978             if(this.active){
37979                 this.active.hide();
37980             }
37981             this.active = this.items[id];
37982             this.active.show();
37983             this.fireEvent("tabchange", this, this.active);
37984         }
37985         return tab;
37986     },
37987
37988     /**
37989      * Gets the active {@link Roo.TabPanelItem}.
37990      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37991      */
37992     getActiveTab : function(){
37993         return this.active;
37994     },
37995
37996     /**
37997      * Updates the tab body element to fit the height of the container element
37998      * for overflow scrolling
37999      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38000      */
38001     syncHeight : function(targetHeight){
38002         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38003         var bm = this.bodyEl.getMargins();
38004         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38005         this.bodyEl.setHeight(newHeight);
38006         return newHeight;
38007     },
38008
38009     onResize : function(){
38010         if(this.monitorResize){
38011             this.autoSizeTabs();
38012         }
38013     },
38014
38015     /**
38016      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38017      */
38018     beginUpdate : function(){
38019         this.updating = true;
38020     },
38021
38022     /**
38023      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38024      */
38025     endUpdate : function(){
38026         this.updating = false;
38027         this.autoSizeTabs();
38028     },
38029
38030     /**
38031      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38032      */
38033     autoSizeTabs : function(){
38034         var count = this.items.length;
38035         var vcount = count - this.hiddenCount;
38036         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38037             return;
38038         }
38039         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38040         var availWidth = Math.floor(w / vcount);
38041         var b = this.stripBody;
38042         if(b.getWidth() > w){
38043             var tabs = this.items;
38044             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38045             if(availWidth < this.minTabWidth){
38046                 /*if(!this.sleft){    // incomplete scrolling code
38047                     this.createScrollButtons();
38048                 }
38049                 this.showScroll();
38050                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38051             }
38052         }else{
38053             if(this.currentTabWidth < this.preferredTabWidth){
38054                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38055             }
38056         }
38057     },
38058
38059     /**
38060      * Returns the number of tabs in this TabPanel.
38061      * @return {Number}
38062      */
38063      getCount : function(){
38064          return this.items.length;
38065      },
38066
38067     /**
38068      * Resizes all the tabs to the passed width
38069      * @param {Number} The new width
38070      */
38071     setTabWidth : function(width){
38072         this.currentTabWidth = width;
38073         for(var i = 0, len = this.items.length; i < len; i++) {
38074                 if(!this.items[i].isHidden()) {
38075                 this.items[i].setWidth(width);
38076             }
38077         }
38078     },
38079
38080     /**
38081      * Destroys this TabPanel
38082      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38083      */
38084     destroy : function(removeEl){
38085         Roo.EventManager.removeResizeListener(this.onResize, this);
38086         for(var i = 0, len = this.items.length; i < len; i++){
38087             this.items[i].purgeListeners();
38088         }
38089         if(removeEl === true){
38090             this.el.update("");
38091             this.el.remove();
38092         }
38093     },
38094     
38095     createStrip : function(container)
38096     {
38097         var strip = document.createElement("nav");
38098         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38099         container.appendChild(strip);
38100         return strip;
38101     },
38102     
38103     createStripList : function(strip)
38104     {
38105         // div wrapper for retard IE
38106         // returns the "tr" element.
38107         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38108         //'<div class="x-tabs-strip-wrap">'+
38109           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38110           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38111         return strip.firstChild; //.firstChild.firstChild.firstChild;
38112     },
38113     createBody : function(container)
38114     {
38115         var body = document.createElement("div");
38116         Roo.id(body, "tab-body");
38117         //Roo.fly(body).addClass("x-tabs-body");
38118         Roo.fly(body).addClass("tab-content");
38119         container.appendChild(body);
38120         return body;
38121     },
38122     createItemBody :function(bodyEl, id){
38123         var body = Roo.getDom(id);
38124         if(!body){
38125             body = document.createElement("div");
38126             body.id = id;
38127         }
38128         //Roo.fly(body).addClass("x-tabs-item-body");
38129         Roo.fly(body).addClass("tab-pane");
38130          bodyEl.insertBefore(body, bodyEl.firstChild);
38131         return body;
38132     },
38133     /** @private */
38134     createStripElements :  function(stripEl, text, closable, tpl)
38135     {
38136         var td = document.createElement("li"); // was td..
38137         
38138         
38139         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38140         
38141         
38142         stripEl.appendChild(td);
38143         /*if(closable){
38144             td.className = "x-tabs-closable";
38145             if(!this.closeTpl){
38146                 this.closeTpl = new Roo.Template(
38147                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38148                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38149                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38150                 );
38151             }
38152             var el = this.closeTpl.overwrite(td, {"text": text});
38153             var close = el.getElementsByTagName("div")[0];
38154             var inner = el.getElementsByTagName("em")[0];
38155             return {"el": el, "close": close, "inner": inner};
38156         } else {
38157         */
38158         // not sure what this is..
38159 //            if(!this.tabTpl){
38160                 //this.tabTpl = new Roo.Template(
38161                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38162                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38163                 //);
38164 //                this.tabTpl = new Roo.Template(
38165 //                   '<a href="#">' +
38166 //                   '<span unselectable="on"' +
38167 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38168 //                            ' >{text}</span></a>'
38169 //                );
38170 //                
38171 //            }
38172
38173
38174             var template = tpl || this.tabTpl || false;
38175             
38176             if(!template){
38177                 
38178                 template = new Roo.Template(
38179                    '<a href="#">' +
38180                    '<span unselectable="on"' +
38181                             (this.disableTooltips ? '' : ' title="{text}"') +
38182                             ' >{text}</span></a>'
38183                 );
38184             }
38185             
38186             switch (typeof(template)) {
38187                 case 'object' :
38188                     break;
38189                 case 'string' :
38190                     template = new Roo.Template(template);
38191                     break;
38192                 default :
38193                     break;
38194             }
38195             
38196             var el = template.overwrite(td, {"text": text});
38197             
38198             var inner = el.getElementsByTagName("span")[0];
38199             
38200             return {"el": el, "inner": inner};
38201             
38202     }
38203         
38204     
38205 });
38206
38207 /**
38208  * @class Roo.TabPanelItem
38209  * @extends Roo.util.Observable
38210  * Represents an individual item (tab plus body) in a TabPanel.
38211  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38212  * @param {String} id The id of this TabPanelItem
38213  * @param {String} text The text for the tab of this TabPanelItem
38214  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38215  */
38216 Roo.bootstrap.panel.TabItem = function(config){
38217     /**
38218      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38219      * @type Roo.TabPanel
38220      */
38221     this.tabPanel = config.panel;
38222     /**
38223      * The id for this TabPanelItem
38224      * @type String
38225      */
38226     this.id = config.id;
38227     /** @private */
38228     this.disabled = false;
38229     /** @private */
38230     this.text = config.text;
38231     /** @private */
38232     this.loaded = false;
38233     this.closable = config.closable;
38234
38235     /**
38236      * The body element for this TabPanelItem.
38237      * @type Roo.Element
38238      */
38239     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38240     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38241     this.bodyEl.setStyle("display", "block");
38242     this.bodyEl.setStyle("zoom", "1");
38243     //this.hideAction();
38244
38245     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38246     /** @private */
38247     this.el = Roo.get(els.el);
38248     this.inner = Roo.get(els.inner, true);
38249     this.textEl = Roo.get(this.el.dom.firstChild, true);
38250     this.pnode = Roo.get(els.el.parentNode, true);
38251 //    this.el.on("mousedown", this.onTabMouseDown, this);
38252     this.el.on("click", this.onTabClick, this);
38253     /** @private */
38254     if(config.closable){
38255         var c = Roo.get(els.close, true);
38256         c.dom.title = this.closeText;
38257         c.addClassOnOver("close-over");
38258         c.on("click", this.closeClick, this);
38259      }
38260
38261     this.addEvents({
38262          /**
38263          * @event activate
38264          * Fires when this tab becomes the active tab.
38265          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38266          * @param {Roo.TabPanelItem} this
38267          */
38268         "activate": true,
38269         /**
38270          * @event beforeclose
38271          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38272          * @param {Roo.TabPanelItem} this
38273          * @param {Object} e Set cancel to true on this object to cancel the close.
38274          */
38275         "beforeclose": true,
38276         /**
38277          * @event close
38278          * Fires when this tab is closed.
38279          * @param {Roo.TabPanelItem} this
38280          */
38281          "close": true,
38282         /**
38283          * @event deactivate
38284          * Fires when this tab is no longer the active tab.
38285          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38286          * @param {Roo.TabPanelItem} this
38287          */
38288          "deactivate" : true
38289     });
38290     this.hidden = false;
38291
38292     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38293 };
38294
38295 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38296            {
38297     purgeListeners : function(){
38298        Roo.util.Observable.prototype.purgeListeners.call(this);
38299        this.el.removeAllListeners();
38300     },
38301     /**
38302      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38303      */
38304     show : function(){
38305         this.pnode.addClass("active");
38306         this.showAction();
38307         if(Roo.isOpera){
38308             this.tabPanel.stripWrap.repaint();
38309         }
38310         this.fireEvent("activate", this.tabPanel, this);
38311     },
38312
38313     /**
38314      * Returns true if this tab is the active tab.
38315      * @return {Boolean}
38316      */
38317     isActive : function(){
38318         return this.tabPanel.getActiveTab() == this;
38319     },
38320
38321     /**
38322      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38323      */
38324     hide : function(){
38325         this.pnode.removeClass("active");
38326         this.hideAction();
38327         this.fireEvent("deactivate", this.tabPanel, this);
38328     },
38329
38330     hideAction : function(){
38331         this.bodyEl.hide();
38332         this.bodyEl.setStyle("position", "absolute");
38333         this.bodyEl.setLeft("-20000px");
38334         this.bodyEl.setTop("-20000px");
38335     },
38336
38337     showAction : function(){
38338         this.bodyEl.setStyle("position", "relative");
38339         this.bodyEl.setTop("");
38340         this.bodyEl.setLeft("");
38341         this.bodyEl.show();
38342     },
38343
38344     /**
38345      * Set the tooltip for the tab.
38346      * @param {String} tooltip The tab's tooltip
38347      */
38348     setTooltip : function(text){
38349         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38350             this.textEl.dom.qtip = text;
38351             this.textEl.dom.removeAttribute('title');
38352         }else{
38353             this.textEl.dom.title = text;
38354         }
38355     },
38356
38357     onTabClick : function(e){
38358         e.preventDefault();
38359         this.tabPanel.activate(this.id);
38360     },
38361
38362     onTabMouseDown : function(e){
38363         e.preventDefault();
38364         this.tabPanel.activate(this.id);
38365     },
38366 /*
38367     getWidth : function(){
38368         return this.inner.getWidth();
38369     },
38370
38371     setWidth : function(width){
38372         var iwidth = width - this.pnode.getPadding("lr");
38373         this.inner.setWidth(iwidth);
38374         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38375         this.pnode.setWidth(width);
38376     },
38377 */
38378     /**
38379      * Show or hide the tab
38380      * @param {Boolean} hidden True to hide or false to show.
38381      */
38382     setHidden : function(hidden){
38383         this.hidden = hidden;
38384         this.pnode.setStyle("display", hidden ? "none" : "");
38385     },
38386
38387     /**
38388      * Returns true if this tab is "hidden"
38389      * @return {Boolean}
38390      */
38391     isHidden : function(){
38392         return this.hidden;
38393     },
38394
38395     /**
38396      * Returns the text for this tab
38397      * @return {String}
38398      */
38399     getText : function(){
38400         return this.text;
38401     },
38402     /*
38403     autoSize : function(){
38404         //this.el.beginMeasure();
38405         this.textEl.setWidth(1);
38406         /*
38407          *  #2804 [new] Tabs in Roojs
38408          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38409          */
38410         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38411         //this.el.endMeasure();
38412     //},
38413
38414     /**
38415      * Sets the text for the tab (Note: this also sets the tooltip text)
38416      * @param {String} text The tab's text and tooltip
38417      */
38418     setText : function(text){
38419         this.text = text;
38420         this.textEl.update(text);
38421         this.setTooltip(text);
38422         //if(!this.tabPanel.resizeTabs){
38423         //    this.autoSize();
38424         //}
38425     },
38426     /**
38427      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38428      */
38429     activate : function(){
38430         this.tabPanel.activate(this.id);
38431     },
38432
38433     /**
38434      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38435      */
38436     disable : function(){
38437         if(this.tabPanel.active != this){
38438             this.disabled = true;
38439             this.pnode.addClass("disabled");
38440         }
38441     },
38442
38443     /**
38444      * Enables this TabPanelItem if it was previously disabled.
38445      */
38446     enable : function(){
38447         this.disabled = false;
38448         this.pnode.removeClass("disabled");
38449     },
38450
38451     /**
38452      * Sets the content for this TabPanelItem.
38453      * @param {String} content The content
38454      * @param {Boolean} loadScripts true to look for and load scripts
38455      */
38456     setContent : function(content, loadScripts){
38457         this.bodyEl.update(content, loadScripts);
38458     },
38459
38460     /**
38461      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38462      * @return {Roo.UpdateManager} The UpdateManager
38463      */
38464     getUpdateManager : function(){
38465         return this.bodyEl.getUpdateManager();
38466     },
38467
38468     /**
38469      * Set a URL to be used to load the content for this TabPanelItem.
38470      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38471      * @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)
38472      * @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)
38473      * @return {Roo.UpdateManager} The UpdateManager
38474      */
38475     setUrl : function(url, params, loadOnce){
38476         if(this.refreshDelegate){
38477             this.un('activate', this.refreshDelegate);
38478         }
38479         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38480         this.on("activate", this.refreshDelegate);
38481         return this.bodyEl.getUpdateManager();
38482     },
38483
38484     /** @private */
38485     _handleRefresh : function(url, params, loadOnce){
38486         if(!loadOnce || !this.loaded){
38487             var updater = this.bodyEl.getUpdateManager();
38488             updater.update(url, params, this._setLoaded.createDelegate(this));
38489         }
38490     },
38491
38492     /**
38493      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38494      *   Will fail silently if the setUrl method has not been called.
38495      *   This does not activate the panel, just updates its content.
38496      */
38497     refresh : function(){
38498         if(this.refreshDelegate){
38499            this.loaded = false;
38500            this.refreshDelegate();
38501         }
38502     },
38503
38504     /** @private */
38505     _setLoaded : function(){
38506         this.loaded = true;
38507     },
38508
38509     /** @private */
38510     closeClick : function(e){
38511         var o = {};
38512         e.stopEvent();
38513         this.fireEvent("beforeclose", this, o);
38514         if(o.cancel !== true){
38515             this.tabPanel.removeTab(this.id);
38516         }
38517     },
38518     /**
38519      * The text displayed in the tooltip for the close icon.
38520      * @type String
38521      */
38522     closeText : "Close this tab"
38523 });
38524 /**
38525 *    This script refer to:
38526 *    Title: International Telephone Input
38527 *    Author: Jack O'Connor
38528 *    Code version:  v12.1.12
38529 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38530 **/
38531
38532 Roo.bootstrap.PhoneInputData = function() {
38533     var d = [
38534       [
38535         "Afghanistan (‫افغانستان‬‎)",
38536         "af",
38537         "93"
38538       ],
38539       [
38540         "Albania (Shqipëri)",
38541         "al",
38542         "355"
38543       ],
38544       [
38545         "Algeria (‫الجزائر‬‎)",
38546         "dz",
38547         "213"
38548       ],
38549       [
38550         "American Samoa",
38551         "as",
38552         "1684"
38553       ],
38554       [
38555         "Andorra",
38556         "ad",
38557         "376"
38558       ],
38559       [
38560         "Angola",
38561         "ao",
38562         "244"
38563       ],
38564       [
38565         "Anguilla",
38566         "ai",
38567         "1264"
38568       ],
38569       [
38570         "Antigua and Barbuda",
38571         "ag",
38572         "1268"
38573       ],
38574       [
38575         "Argentina",
38576         "ar",
38577         "54"
38578       ],
38579       [
38580         "Armenia (Հայաստան)",
38581         "am",
38582         "374"
38583       ],
38584       [
38585         "Aruba",
38586         "aw",
38587         "297"
38588       ],
38589       [
38590         "Australia",
38591         "au",
38592         "61",
38593         0
38594       ],
38595       [
38596         "Austria (Österreich)",
38597         "at",
38598         "43"
38599       ],
38600       [
38601         "Azerbaijan (Azərbaycan)",
38602         "az",
38603         "994"
38604       ],
38605       [
38606         "Bahamas",
38607         "bs",
38608         "1242"
38609       ],
38610       [
38611         "Bahrain (‫البحرين‬‎)",
38612         "bh",
38613         "973"
38614       ],
38615       [
38616         "Bangladesh (বাংলাদেশ)",
38617         "bd",
38618         "880"
38619       ],
38620       [
38621         "Barbados",
38622         "bb",
38623         "1246"
38624       ],
38625       [
38626         "Belarus (Беларусь)",
38627         "by",
38628         "375"
38629       ],
38630       [
38631         "Belgium (België)",
38632         "be",
38633         "32"
38634       ],
38635       [
38636         "Belize",
38637         "bz",
38638         "501"
38639       ],
38640       [
38641         "Benin (Bénin)",
38642         "bj",
38643         "229"
38644       ],
38645       [
38646         "Bermuda",
38647         "bm",
38648         "1441"
38649       ],
38650       [
38651         "Bhutan (འབྲུག)",
38652         "bt",
38653         "975"
38654       ],
38655       [
38656         "Bolivia",
38657         "bo",
38658         "591"
38659       ],
38660       [
38661         "Bosnia and Herzegovina (Босна и Херцеговина)",
38662         "ba",
38663         "387"
38664       ],
38665       [
38666         "Botswana",
38667         "bw",
38668         "267"
38669       ],
38670       [
38671         "Brazil (Brasil)",
38672         "br",
38673         "55"
38674       ],
38675       [
38676         "British Indian Ocean Territory",
38677         "io",
38678         "246"
38679       ],
38680       [
38681         "British Virgin Islands",
38682         "vg",
38683         "1284"
38684       ],
38685       [
38686         "Brunei",
38687         "bn",
38688         "673"
38689       ],
38690       [
38691         "Bulgaria (България)",
38692         "bg",
38693         "359"
38694       ],
38695       [
38696         "Burkina Faso",
38697         "bf",
38698         "226"
38699       ],
38700       [
38701         "Burundi (Uburundi)",
38702         "bi",
38703         "257"
38704       ],
38705       [
38706         "Cambodia (កម្ពុជា)",
38707         "kh",
38708         "855"
38709       ],
38710       [
38711         "Cameroon (Cameroun)",
38712         "cm",
38713         "237"
38714       ],
38715       [
38716         "Canada",
38717         "ca",
38718         "1",
38719         1,
38720         ["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"]
38721       ],
38722       [
38723         "Cape Verde (Kabu Verdi)",
38724         "cv",
38725         "238"
38726       ],
38727       [
38728         "Caribbean Netherlands",
38729         "bq",
38730         "599",
38731         1
38732       ],
38733       [
38734         "Cayman Islands",
38735         "ky",
38736         "1345"
38737       ],
38738       [
38739         "Central African Republic (République centrafricaine)",
38740         "cf",
38741         "236"
38742       ],
38743       [
38744         "Chad (Tchad)",
38745         "td",
38746         "235"
38747       ],
38748       [
38749         "Chile",
38750         "cl",
38751         "56"
38752       ],
38753       [
38754         "China (中国)",
38755         "cn",
38756         "86"
38757       ],
38758       [
38759         "Christmas Island",
38760         "cx",
38761         "61",
38762         2
38763       ],
38764       [
38765         "Cocos (Keeling) Islands",
38766         "cc",
38767         "61",
38768         1
38769       ],
38770       [
38771         "Colombia",
38772         "co",
38773         "57"
38774       ],
38775       [
38776         "Comoros (‫جزر القمر‬‎)",
38777         "km",
38778         "269"
38779       ],
38780       [
38781         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38782         "cd",
38783         "243"
38784       ],
38785       [
38786         "Congo (Republic) (Congo-Brazzaville)",
38787         "cg",
38788         "242"
38789       ],
38790       [
38791         "Cook Islands",
38792         "ck",
38793         "682"
38794       ],
38795       [
38796         "Costa Rica",
38797         "cr",
38798         "506"
38799       ],
38800       [
38801         "Côte d’Ivoire",
38802         "ci",
38803         "225"
38804       ],
38805       [
38806         "Croatia (Hrvatska)",
38807         "hr",
38808         "385"
38809       ],
38810       [
38811         "Cuba",
38812         "cu",
38813         "53"
38814       ],
38815       [
38816         "Curaçao",
38817         "cw",
38818         "599",
38819         0
38820       ],
38821       [
38822         "Cyprus (Κύπρος)",
38823         "cy",
38824         "357"
38825       ],
38826       [
38827         "Czech Republic (Česká republika)",
38828         "cz",
38829         "420"
38830       ],
38831       [
38832         "Denmark (Danmark)",
38833         "dk",
38834         "45"
38835       ],
38836       [
38837         "Djibouti",
38838         "dj",
38839         "253"
38840       ],
38841       [
38842         "Dominica",
38843         "dm",
38844         "1767"
38845       ],
38846       [
38847         "Dominican Republic (República Dominicana)",
38848         "do",
38849         "1",
38850         2,
38851         ["809", "829", "849"]
38852       ],
38853       [
38854         "Ecuador",
38855         "ec",
38856         "593"
38857       ],
38858       [
38859         "Egypt (‫مصر‬‎)",
38860         "eg",
38861         "20"
38862       ],
38863       [
38864         "El Salvador",
38865         "sv",
38866         "503"
38867       ],
38868       [
38869         "Equatorial Guinea (Guinea Ecuatorial)",
38870         "gq",
38871         "240"
38872       ],
38873       [
38874         "Eritrea",
38875         "er",
38876         "291"
38877       ],
38878       [
38879         "Estonia (Eesti)",
38880         "ee",
38881         "372"
38882       ],
38883       [
38884         "Ethiopia",
38885         "et",
38886         "251"
38887       ],
38888       [
38889         "Falkland Islands (Islas Malvinas)",
38890         "fk",
38891         "500"
38892       ],
38893       [
38894         "Faroe Islands (Føroyar)",
38895         "fo",
38896         "298"
38897       ],
38898       [
38899         "Fiji",
38900         "fj",
38901         "679"
38902       ],
38903       [
38904         "Finland (Suomi)",
38905         "fi",
38906         "358",
38907         0
38908       ],
38909       [
38910         "France",
38911         "fr",
38912         "33"
38913       ],
38914       [
38915         "French Guiana (Guyane française)",
38916         "gf",
38917         "594"
38918       ],
38919       [
38920         "French Polynesia (Polynésie française)",
38921         "pf",
38922         "689"
38923       ],
38924       [
38925         "Gabon",
38926         "ga",
38927         "241"
38928       ],
38929       [
38930         "Gambia",
38931         "gm",
38932         "220"
38933       ],
38934       [
38935         "Georgia (საქართველო)",
38936         "ge",
38937         "995"
38938       ],
38939       [
38940         "Germany (Deutschland)",
38941         "de",
38942         "49"
38943       ],
38944       [
38945         "Ghana (Gaana)",
38946         "gh",
38947         "233"
38948       ],
38949       [
38950         "Gibraltar",
38951         "gi",
38952         "350"
38953       ],
38954       [
38955         "Greece (Ελλάδα)",
38956         "gr",
38957         "30"
38958       ],
38959       [
38960         "Greenland (Kalaallit Nunaat)",
38961         "gl",
38962         "299"
38963       ],
38964       [
38965         "Grenada",
38966         "gd",
38967         "1473"
38968       ],
38969       [
38970         "Guadeloupe",
38971         "gp",
38972         "590",
38973         0
38974       ],
38975       [
38976         "Guam",
38977         "gu",
38978         "1671"
38979       ],
38980       [
38981         "Guatemala",
38982         "gt",
38983         "502"
38984       ],
38985       [
38986         "Guernsey",
38987         "gg",
38988         "44",
38989         1
38990       ],
38991       [
38992         "Guinea (Guinée)",
38993         "gn",
38994         "224"
38995       ],
38996       [
38997         "Guinea-Bissau (Guiné Bissau)",
38998         "gw",
38999         "245"
39000       ],
39001       [
39002         "Guyana",
39003         "gy",
39004         "592"
39005       ],
39006       [
39007         "Haiti",
39008         "ht",
39009         "509"
39010       ],
39011       [
39012         "Honduras",
39013         "hn",
39014         "504"
39015       ],
39016       [
39017         "Hong Kong (香港)",
39018         "hk",
39019         "852"
39020       ],
39021       [
39022         "Hungary (Magyarország)",
39023         "hu",
39024         "36"
39025       ],
39026       [
39027         "Iceland (Ísland)",
39028         "is",
39029         "354"
39030       ],
39031       [
39032         "India (भारत)",
39033         "in",
39034         "91"
39035       ],
39036       [
39037         "Indonesia",
39038         "id",
39039         "62"
39040       ],
39041       [
39042         "Iran (‫ایران‬‎)",
39043         "ir",
39044         "98"
39045       ],
39046       [
39047         "Iraq (‫العراق‬‎)",
39048         "iq",
39049         "964"
39050       ],
39051       [
39052         "Ireland",
39053         "ie",
39054         "353"
39055       ],
39056       [
39057         "Isle of Man",
39058         "im",
39059         "44",
39060         2
39061       ],
39062       [
39063         "Israel (‫ישראל‬‎)",
39064         "il",
39065         "972"
39066       ],
39067       [
39068         "Italy (Italia)",
39069         "it",
39070         "39",
39071         0
39072       ],
39073       [
39074         "Jamaica",
39075         "jm",
39076         "1876"
39077       ],
39078       [
39079         "Japan (日本)",
39080         "jp",
39081         "81"
39082       ],
39083       [
39084         "Jersey",
39085         "je",
39086         "44",
39087         3
39088       ],
39089       [
39090         "Jordan (‫الأردن‬‎)",
39091         "jo",
39092         "962"
39093       ],
39094       [
39095         "Kazakhstan (Казахстан)",
39096         "kz",
39097         "7",
39098         1
39099       ],
39100       [
39101         "Kenya",
39102         "ke",
39103         "254"
39104       ],
39105       [
39106         "Kiribati",
39107         "ki",
39108         "686"
39109       ],
39110       [
39111         "Kosovo",
39112         "xk",
39113         "383"
39114       ],
39115       [
39116         "Kuwait (‫الكويت‬‎)",
39117         "kw",
39118         "965"
39119       ],
39120       [
39121         "Kyrgyzstan (Кыргызстан)",
39122         "kg",
39123         "996"
39124       ],
39125       [
39126         "Laos (ລາວ)",
39127         "la",
39128         "856"
39129       ],
39130       [
39131         "Latvia (Latvija)",
39132         "lv",
39133         "371"
39134       ],
39135       [
39136         "Lebanon (‫لبنان‬‎)",
39137         "lb",
39138         "961"
39139       ],
39140       [
39141         "Lesotho",
39142         "ls",
39143         "266"
39144       ],
39145       [
39146         "Liberia",
39147         "lr",
39148         "231"
39149       ],
39150       [
39151         "Libya (‫ليبيا‬‎)",
39152         "ly",
39153         "218"
39154       ],
39155       [
39156         "Liechtenstein",
39157         "li",
39158         "423"
39159       ],
39160       [
39161         "Lithuania (Lietuva)",
39162         "lt",
39163         "370"
39164       ],
39165       [
39166         "Luxembourg",
39167         "lu",
39168         "352"
39169       ],
39170       [
39171         "Macau (澳門)",
39172         "mo",
39173         "853"
39174       ],
39175       [
39176         "Macedonia (FYROM) (Македонија)",
39177         "mk",
39178         "389"
39179       ],
39180       [
39181         "Madagascar (Madagasikara)",
39182         "mg",
39183         "261"
39184       ],
39185       [
39186         "Malawi",
39187         "mw",
39188         "265"
39189       ],
39190       [
39191         "Malaysia",
39192         "my",
39193         "60"
39194       ],
39195       [
39196         "Maldives",
39197         "mv",
39198         "960"
39199       ],
39200       [
39201         "Mali",
39202         "ml",
39203         "223"
39204       ],
39205       [
39206         "Malta",
39207         "mt",
39208         "356"
39209       ],
39210       [
39211         "Marshall Islands",
39212         "mh",
39213         "692"
39214       ],
39215       [
39216         "Martinique",
39217         "mq",
39218         "596"
39219       ],
39220       [
39221         "Mauritania (‫موريتانيا‬‎)",
39222         "mr",
39223         "222"
39224       ],
39225       [
39226         "Mauritius (Moris)",
39227         "mu",
39228         "230"
39229       ],
39230       [
39231         "Mayotte",
39232         "yt",
39233         "262",
39234         1
39235       ],
39236       [
39237         "Mexico (México)",
39238         "mx",
39239         "52"
39240       ],
39241       [
39242         "Micronesia",
39243         "fm",
39244         "691"
39245       ],
39246       [
39247         "Moldova (Republica Moldova)",
39248         "md",
39249         "373"
39250       ],
39251       [
39252         "Monaco",
39253         "mc",
39254         "377"
39255       ],
39256       [
39257         "Mongolia (Монгол)",
39258         "mn",
39259         "976"
39260       ],
39261       [
39262         "Montenegro (Crna Gora)",
39263         "me",
39264         "382"
39265       ],
39266       [
39267         "Montserrat",
39268         "ms",
39269         "1664"
39270       ],
39271       [
39272         "Morocco (‫المغرب‬‎)",
39273         "ma",
39274         "212",
39275         0
39276       ],
39277       [
39278         "Mozambique (Moçambique)",
39279         "mz",
39280         "258"
39281       ],
39282       [
39283         "Myanmar (Burma) (မြန်မာ)",
39284         "mm",
39285         "95"
39286       ],
39287       [
39288         "Namibia (Namibië)",
39289         "na",
39290         "264"
39291       ],
39292       [
39293         "Nauru",
39294         "nr",
39295         "674"
39296       ],
39297       [
39298         "Nepal (नेपाल)",
39299         "np",
39300         "977"
39301       ],
39302       [
39303         "Netherlands (Nederland)",
39304         "nl",
39305         "31"
39306       ],
39307       [
39308         "New Caledonia (Nouvelle-Calédonie)",
39309         "nc",
39310         "687"
39311       ],
39312       [
39313         "New Zealand",
39314         "nz",
39315         "64"
39316       ],
39317       [
39318         "Nicaragua",
39319         "ni",
39320         "505"
39321       ],
39322       [
39323         "Niger (Nijar)",
39324         "ne",
39325         "227"
39326       ],
39327       [
39328         "Nigeria",
39329         "ng",
39330         "234"
39331       ],
39332       [
39333         "Niue",
39334         "nu",
39335         "683"
39336       ],
39337       [
39338         "Norfolk Island",
39339         "nf",
39340         "672"
39341       ],
39342       [
39343         "North Korea (조선 민주주의 인민 공화국)",
39344         "kp",
39345         "850"
39346       ],
39347       [
39348         "Northern Mariana Islands",
39349         "mp",
39350         "1670"
39351       ],
39352       [
39353         "Norway (Norge)",
39354         "no",
39355         "47",
39356         0
39357       ],
39358       [
39359         "Oman (‫عُمان‬‎)",
39360         "om",
39361         "968"
39362       ],
39363       [
39364         "Pakistan (‫پاکستان‬‎)",
39365         "pk",
39366         "92"
39367       ],
39368       [
39369         "Palau",
39370         "pw",
39371         "680"
39372       ],
39373       [
39374         "Palestine (‫فلسطين‬‎)",
39375         "ps",
39376         "970"
39377       ],
39378       [
39379         "Panama (Panamá)",
39380         "pa",
39381         "507"
39382       ],
39383       [
39384         "Papua New Guinea",
39385         "pg",
39386         "675"
39387       ],
39388       [
39389         "Paraguay",
39390         "py",
39391         "595"
39392       ],
39393       [
39394         "Peru (Perú)",
39395         "pe",
39396         "51"
39397       ],
39398       [
39399         "Philippines",
39400         "ph",
39401         "63"
39402       ],
39403       [
39404         "Poland (Polska)",
39405         "pl",
39406         "48"
39407       ],
39408       [
39409         "Portugal",
39410         "pt",
39411         "351"
39412       ],
39413       [
39414         "Puerto Rico",
39415         "pr",
39416         "1",
39417         3,
39418         ["787", "939"]
39419       ],
39420       [
39421         "Qatar (‫قطر‬‎)",
39422         "qa",
39423         "974"
39424       ],
39425       [
39426         "Réunion (La Réunion)",
39427         "re",
39428         "262",
39429         0
39430       ],
39431       [
39432         "Romania (România)",
39433         "ro",
39434         "40"
39435       ],
39436       [
39437         "Russia (Россия)",
39438         "ru",
39439         "7",
39440         0
39441       ],
39442       [
39443         "Rwanda",
39444         "rw",
39445         "250"
39446       ],
39447       [
39448         "Saint Barthélemy",
39449         "bl",
39450         "590",
39451         1
39452       ],
39453       [
39454         "Saint Helena",
39455         "sh",
39456         "290"
39457       ],
39458       [
39459         "Saint Kitts and Nevis",
39460         "kn",
39461         "1869"
39462       ],
39463       [
39464         "Saint Lucia",
39465         "lc",
39466         "1758"
39467       ],
39468       [
39469         "Saint Martin (Saint-Martin (partie française))",
39470         "mf",
39471         "590",
39472         2
39473       ],
39474       [
39475         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39476         "pm",
39477         "508"
39478       ],
39479       [
39480         "Saint Vincent and the Grenadines",
39481         "vc",
39482         "1784"
39483       ],
39484       [
39485         "Samoa",
39486         "ws",
39487         "685"
39488       ],
39489       [
39490         "San Marino",
39491         "sm",
39492         "378"
39493       ],
39494       [
39495         "São Tomé and Príncipe (São Tomé e Príncipe)",
39496         "st",
39497         "239"
39498       ],
39499       [
39500         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39501         "sa",
39502         "966"
39503       ],
39504       [
39505         "Senegal (Sénégal)",
39506         "sn",
39507         "221"
39508       ],
39509       [
39510         "Serbia (Србија)",
39511         "rs",
39512         "381"
39513       ],
39514       [
39515         "Seychelles",
39516         "sc",
39517         "248"
39518       ],
39519       [
39520         "Sierra Leone",
39521         "sl",
39522         "232"
39523       ],
39524       [
39525         "Singapore",
39526         "sg",
39527         "65"
39528       ],
39529       [
39530         "Sint Maarten",
39531         "sx",
39532         "1721"
39533       ],
39534       [
39535         "Slovakia (Slovensko)",
39536         "sk",
39537         "421"
39538       ],
39539       [
39540         "Slovenia (Slovenija)",
39541         "si",
39542         "386"
39543       ],
39544       [
39545         "Solomon Islands",
39546         "sb",
39547         "677"
39548       ],
39549       [
39550         "Somalia (Soomaaliya)",
39551         "so",
39552         "252"
39553       ],
39554       [
39555         "South Africa",
39556         "za",
39557         "27"
39558       ],
39559       [
39560         "South Korea (대한민국)",
39561         "kr",
39562         "82"
39563       ],
39564       [
39565         "South Sudan (‫جنوب السودان‬‎)",
39566         "ss",
39567         "211"
39568       ],
39569       [
39570         "Spain (España)",
39571         "es",
39572         "34"
39573       ],
39574       [
39575         "Sri Lanka (ශ්‍රී ලංකාව)",
39576         "lk",
39577         "94"
39578       ],
39579       [
39580         "Sudan (‫السودان‬‎)",
39581         "sd",
39582         "249"
39583       ],
39584       [
39585         "Suriname",
39586         "sr",
39587         "597"
39588       ],
39589       [
39590         "Svalbard and Jan Mayen",
39591         "sj",
39592         "47",
39593         1
39594       ],
39595       [
39596         "Swaziland",
39597         "sz",
39598         "268"
39599       ],
39600       [
39601         "Sweden (Sverige)",
39602         "se",
39603         "46"
39604       ],
39605       [
39606         "Switzerland (Schweiz)",
39607         "ch",
39608         "41"
39609       ],
39610       [
39611         "Syria (‫سوريا‬‎)",
39612         "sy",
39613         "963"
39614       ],
39615       [
39616         "Taiwan (台灣)",
39617         "tw",
39618         "886"
39619       ],
39620       [
39621         "Tajikistan",
39622         "tj",
39623         "992"
39624       ],
39625       [
39626         "Tanzania",
39627         "tz",
39628         "255"
39629       ],
39630       [
39631         "Thailand (ไทย)",
39632         "th",
39633         "66"
39634       ],
39635       [
39636         "Timor-Leste",
39637         "tl",
39638         "670"
39639       ],
39640       [
39641         "Togo",
39642         "tg",
39643         "228"
39644       ],
39645       [
39646         "Tokelau",
39647         "tk",
39648         "690"
39649       ],
39650       [
39651         "Tonga",
39652         "to",
39653         "676"
39654       ],
39655       [
39656         "Trinidad and Tobago",
39657         "tt",
39658         "1868"
39659       ],
39660       [
39661         "Tunisia (‫تونس‬‎)",
39662         "tn",
39663         "216"
39664       ],
39665       [
39666         "Turkey (Türkiye)",
39667         "tr",
39668         "90"
39669       ],
39670       [
39671         "Turkmenistan",
39672         "tm",
39673         "993"
39674       ],
39675       [
39676         "Turks and Caicos Islands",
39677         "tc",
39678         "1649"
39679       ],
39680       [
39681         "Tuvalu",
39682         "tv",
39683         "688"
39684       ],
39685       [
39686         "U.S. Virgin Islands",
39687         "vi",
39688         "1340"
39689       ],
39690       [
39691         "Uganda",
39692         "ug",
39693         "256"
39694       ],
39695       [
39696         "Ukraine (Україна)",
39697         "ua",
39698         "380"
39699       ],
39700       [
39701         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39702         "ae",
39703         "971"
39704       ],
39705       [
39706         "United Kingdom",
39707         "gb",
39708         "44",
39709         0
39710       ],
39711       [
39712         "United States",
39713         "us",
39714         "1",
39715         0
39716       ],
39717       [
39718         "Uruguay",
39719         "uy",
39720         "598"
39721       ],
39722       [
39723         "Uzbekistan (Oʻzbekiston)",
39724         "uz",
39725         "998"
39726       ],
39727       [
39728         "Vanuatu",
39729         "vu",
39730         "678"
39731       ],
39732       [
39733         "Vatican City (Città del Vaticano)",
39734         "va",
39735         "39",
39736         1
39737       ],
39738       [
39739         "Venezuela",
39740         "ve",
39741         "58"
39742       ],
39743       [
39744         "Vietnam (Việt Nam)",
39745         "vn",
39746         "84"
39747       ],
39748       [
39749         "Wallis and Futuna (Wallis-et-Futuna)",
39750         "wf",
39751         "681"
39752       ],
39753       [
39754         "Western Sahara (‫الصحراء الغربية‬‎)",
39755         "eh",
39756         "212",
39757         1
39758       ],
39759       [
39760         "Yemen (‫اليمن‬‎)",
39761         "ye",
39762         "967"
39763       ],
39764       [
39765         "Zambia",
39766         "zm",
39767         "260"
39768       ],
39769       [
39770         "Zimbabwe",
39771         "zw",
39772         "263"
39773       ],
39774       [
39775         "Åland Islands",
39776         "ax",
39777         "358",
39778         1
39779       ]
39780   ];
39781   
39782   return d;
39783 }/**
39784 *    This script refer to:
39785 *    Title: International Telephone Input
39786 *    Author: Jack O'Connor
39787 *    Code version:  v12.1.12
39788 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39789 **/
39790
39791 /**
39792  * @class Roo.bootstrap.PhoneInput
39793  * @extends Roo.bootstrap.TriggerField
39794  * An input with International dial-code selection
39795  
39796  * @cfg {String} defaultDialCode default '+852'
39797  * @cfg {Array} preferedCountries default []
39798   
39799  * @constructor
39800  * Create a new PhoneInput.
39801  * @param {Object} config Configuration options
39802  */
39803
39804 Roo.bootstrap.PhoneInput = function(config) {
39805     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39806 };
39807
39808 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39809         
39810         listWidth: undefined,
39811         
39812         selectedClass: 'active',
39813         
39814         invalidClass : "has-warning",
39815         
39816         validClass: 'has-success',
39817         
39818         allowed: '0123456789',
39819         
39820         /**
39821          * @cfg {String} defaultDialCode The default dial code when initializing the input
39822          */
39823         defaultDialCode: '+852',
39824         
39825         /**
39826          * @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
39827          */
39828         preferedCountries: false,
39829         
39830         getAutoCreate : function()
39831         {
39832             var data = Roo.bootstrap.PhoneInputData();
39833             var align = this.labelAlign || this.parentLabelAlign();
39834             var id = Roo.id();
39835             
39836             this.allCountries = [];
39837             this.dialCodeMapping = [];
39838             
39839             for (var i = 0; i < data.length; i++) {
39840               var c = data[i];
39841               this.allCountries[i] = {
39842                 name: c[0],
39843                 iso2: c[1],
39844                 dialCode: c[2],
39845                 priority: c[3] || 0,
39846                 areaCodes: c[4] || null
39847               };
39848               this.dialCodeMapping[c[2]] = {
39849                   name: c[0],
39850                   iso2: c[1],
39851                   priority: c[3] || 0,
39852                   areaCodes: c[4] || null
39853               };
39854             }
39855             
39856             var cfg = {
39857                 cls: 'form-group',
39858                 cn: []
39859             };
39860             
39861             var input =  {
39862                 tag: 'input',
39863                 id : id,
39864                 cls : 'form-control tel-input',
39865                 autocomplete: 'new-password'
39866             };
39867             
39868             var hiddenInput = {
39869                 tag: 'input',
39870                 type: 'hidden',
39871                 cls: 'hidden-tel-input'
39872             };
39873             
39874             if (this.name) {
39875                 hiddenInput.name = this.name;
39876             }
39877             
39878             if (this.disabled) {
39879                 input.disabled = true;
39880             }
39881             
39882             var flag_container = {
39883                 tag: 'div',
39884                 cls: 'flag-box',
39885                 cn: [
39886                     {
39887                         tag: 'div',
39888                         cls: 'flag'
39889                     },
39890                     {
39891                         tag: 'div',
39892                         cls: 'caret'
39893                     }
39894                 ]
39895             };
39896             
39897             var box = {
39898                 tag: 'div',
39899                 cls: this.hasFeedback ? 'has-feedback' : '',
39900                 cn: [
39901                     hiddenInput,
39902                     input,
39903                     {
39904                         tag: 'input',
39905                         cls: 'dial-code-holder',
39906                         disabled: true
39907                     }
39908                 ]
39909             };
39910             
39911             var container = {
39912                 cls: 'roo-select2-container input-group',
39913                 cn: [
39914                     flag_container,
39915                     box
39916                 ]
39917             };
39918             
39919             if (this.fieldLabel.length) {
39920                 var indicator = {
39921                     tag: 'i',
39922                     tooltip: 'This field is required'
39923                 };
39924                 
39925                 var label = {
39926                     tag: 'label',
39927                     'for':  id,
39928                     cls: 'control-label',
39929                     cn: []
39930                 };
39931                 
39932                 var label_text = {
39933                     tag: 'span',
39934                     html: this.fieldLabel
39935                 };
39936                 
39937                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39938                 label.cn = [
39939                     indicator,
39940                     label_text
39941                 ];
39942                 
39943                 if(this.indicatorpos == 'right') {
39944                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39945                     label.cn = [
39946                         label_text,
39947                         indicator
39948                     ];
39949                 }
39950                 
39951                 if(align == 'left') {
39952                     container = {
39953                         tag: 'div',
39954                         cn: [
39955                             container
39956                         ]
39957                     };
39958                     
39959                     if(this.labelWidth > 12){
39960                         label.style = "width: " + this.labelWidth + 'px';
39961                     }
39962                     if(this.labelWidth < 13 && this.labelmd == 0){
39963                         this.labelmd = this.labelWidth;
39964                     }
39965                     if(this.labellg > 0){
39966                         label.cls += ' col-lg-' + this.labellg;
39967                         input.cls += ' col-lg-' + (12 - this.labellg);
39968                     }
39969                     if(this.labelmd > 0){
39970                         label.cls += ' col-md-' + this.labelmd;
39971                         container.cls += ' col-md-' + (12 - this.labelmd);
39972                     }
39973                     if(this.labelsm > 0){
39974                         label.cls += ' col-sm-' + this.labelsm;
39975                         container.cls += ' col-sm-' + (12 - this.labelsm);
39976                     }
39977                     if(this.labelxs > 0){
39978                         label.cls += ' col-xs-' + this.labelxs;
39979                         container.cls += ' col-xs-' + (12 - this.labelxs);
39980                     }
39981                 }
39982             }
39983             
39984             cfg.cn = [
39985                 label,
39986                 container
39987             ];
39988             
39989             var settings = this;
39990             
39991             ['xs','sm','md','lg'].map(function(size){
39992                 if (settings[size]) {
39993                     cfg.cls += ' col-' + size + '-' + settings[size];
39994                 }
39995             });
39996             
39997             this.store = new Roo.data.Store({
39998                 proxy : new Roo.data.MemoryProxy({}),
39999                 reader : new Roo.data.JsonReader({
40000                     fields : [
40001                         {
40002                             'name' : 'name',
40003                             'type' : 'string'
40004                         },
40005                         {
40006                             'name' : 'iso2',
40007                             'type' : 'string'
40008                         },
40009                         {
40010                             'name' : 'dialCode',
40011                             'type' : 'string'
40012                         },
40013                         {
40014                             'name' : 'priority',
40015                             'type' : 'string'
40016                         },
40017                         {
40018                             'name' : 'areaCodes',
40019                             'type' : 'string'
40020                         }
40021                     ]
40022                 })
40023             });
40024             
40025             if(!this.preferedCountries) {
40026                 this.preferedCountries = [
40027                     'hk',
40028                     'gb',
40029                     'us'
40030                 ];
40031             }
40032             
40033             var p = this.preferedCountries.reverse();
40034             
40035             if(p) {
40036                 for (var i = 0; i < p.length; i++) {
40037                     for (var j = 0; j < this.allCountries.length; j++) {
40038                         if(this.allCountries[j].iso2 == p[i]) {
40039                             var t = this.allCountries[j];
40040                             this.allCountries.splice(j,1);
40041                             this.allCountries.unshift(t);
40042                         }
40043                     } 
40044                 }
40045             }
40046             
40047             this.store.proxy.data = {
40048                 success: true,
40049                 data: this.allCountries
40050             };
40051             
40052             return cfg;
40053         },
40054         
40055         initEvents : function()
40056         {
40057             this.createList();
40058             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40059             
40060             this.indicator = this.indicatorEl();
40061             this.flag = this.flagEl();
40062             this.dialCodeHolder = this.dialCodeHolderEl();
40063             
40064             this.trigger = this.el.select('div.flag-box',true).first();
40065             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40066             
40067             var _this = this;
40068             
40069             (function(){
40070                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40071                 _this.list.setWidth(lw);
40072             }).defer(100);
40073             
40074             this.list.on('mouseover', this.onViewOver, this);
40075             this.list.on('mousemove', this.onViewMove, this);
40076             this.inputEl().on("keyup", this.onKeyUp, this);
40077             
40078             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40079
40080             this.view = new Roo.View(this.list, this.tpl, {
40081                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40082             });
40083             
40084             this.view.on('click', this.onViewClick, this);
40085             this.setValue(this.defaultDialCode);
40086         },
40087         
40088         onTriggerClick : function(e)
40089         {
40090             Roo.log('trigger click');
40091             if(this.disabled){
40092                 return;
40093             }
40094             
40095             if(this.isExpanded()){
40096                 this.collapse();
40097                 this.hasFocus = false;
40098             }else {
40099                 this.store.load({});
40100                 this.hasFocus = true;
40101                 this.expand();
40102             }
40103         },
40104         
40105         isExpanded : function()
40106         {
40107             return this.list.isVisible();
40108         },
40109         
40110         collapse : function()
40111         {
40112             if(!this.isExpanded()){
40113                 return;
40114             }
40115             this.list.hide();
40116             Roo.get(document).un('mousedown', this.collapseIf, this);
40117             Roo.get(document).un('mousewheel', this.collapseIf, this);
40118             this.fireEvent('collapse', this);
40119             this.validate();
40120         },
40121         
40122         expand : function()
40123         {
40124             Roo.log('expand');
40125
40126             if(this.isExpanded() || !this.hasFocus){
40127                 return;
40128             }
40129             
40130             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40131             this.list.setWidth(lw);
40132             
40133             this.list.show();
40134             this.restrictHeight();
40135             
40136             Roo.get(document).on('mousedown', this.collapseIf, this);
40137             Roo.get(document).on('mousewheel', this.collapseIf, this);
40138             
40139             this.fireEvent('expand', this);
40140         },
40141         
40142         restrictHeight : function()
40143         {
40144             this.list.alignTo(this.inputEl(), this.listAlign);
40145             this.list.alignTo(this.inputEl(), this.listAlign);
40146         },
40147         
40148         onViewOver : function(e, t)
40149         {
40150             if(this.inKeyMode){
40151                 return;
40152             }
40153             var item = this.view.findItemFromChild(t);
40154             
40155             if(item){
40156                 var index = this.view.indexOf(item);
40157                 this.select(index, false);
40158             }
40159         },
40160
40161         // private
40162         onViewClick : function(view, doFocus, el, e)
40163         {
40164             var index = this.view.getSelectedIndexes()[0];
40165             
40166             var r = this.store.getAt(index);
40167             
40168             if(r){
40169                 this.onSelect(r, index);
40170             }
40171             if(doFocus !== false && !this.blockFocus){
40172                 this.inputEl().focus();
40173             }
40174         },
40175         
40176         onViewMove : function(e, t)
40177         {
40178             this.inKeyMode = false;
40179         },
40180         
40181         select : function(index, scrollIntoView)
40182         {
40183             this.selectedIndex = index;
40184             this.view.select(index);
40185             if(scrollIntoView !== false){
40186                 var el = this.view.getNode(index);
40187                 if(el){
40188                     this.list.scrollChildIntoView(el, false);
40189                 }
40190             }
40191         },
40192         
40193         createList : function()
40194         {
40195             this.list = Roo.get(document.body).createChild({
40196                 tag: 'ul',
40197                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40198                 style: 'display:none'
40199             });
40200             
40201             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40202         },
40203         
40204         collapseIf : function(e)
40205         {
40206             var in_combo  = e.within(this.el);
40207             var in_list =  e.within(this.list);
40208             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40209             
40210             if (in_combo || in_list || is_list) {
40211                 return;
40212             }
40213             this.collapse();
40214         },
40215         
40216         onSelect : function(record, index)
40217         {
40218             if(this.fireEvent('beforeselect', this, record, index) !== false){
40219                 
40220                 this.setFlagClass(record.data.iso2);
40221                 this.setDialCode(record.data.dialCode);
40222                 this.hasFocus = false;
40223                 this.collapse();
40224                 this.fireEvent('select', this, record, index);
40225             }
40226         },
40227         
40228         flagEl : function()
40229         {
40230             var flag = this.el.select('div.flag',true).first();
40231             if(!flag){
40232                 return false;
40233             }
40234             return flag;
40235         },
40236         
40237         dialCodeHolderEl : function()
40238         {
40239             var d = this.el.select('input.dial-code-holder',true).first();
40240             if(!d){
40241                 return false;
40242             }
40243             return d;
40244         },
40245         
40246         setDialCode : function(v)
40247         {
40248             this.dialCodeHolder.dom.value = '+'+v;
40249         },
40250         
40251         setFlagClass : function(n)
40252         {
40253             this.flag.dom.className = 'flag '+n;
40254         },
40255         
40256         getValue : function()
40257         {
40258             var v = this.inputEl().getValue();
40259             if(this.dialCodeHolder) {
40260                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40261             }
40262             return v;
40263         },
40264         
40265         setValue : function(v)
40266         {
40267             var d = this.getDialCode(v);
40268             
40269             //invalid dial code
40270             if(v.length == 0 || !d || d.length == 0) {
40271                 if(this.rendered){
40272                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40273                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40274                 }
40275                 return;
40276             }
40277             
40278             //valid dial code
40279             this.setFlagClass(this.dialCodeMapping[d].iso2);
40280             this.setDialCode(d);
40281             this.inputEl().dom.value = v.replace('+'+d,'');
40282             this.hiddenEl().dom.value = this.getValue();
40283             
40284             this.validate();
40285         },
40286         
40287         getDialCode : function(v)
40288         {
40289             v = v ||  '';
40290             
40291             if (v.length == 0) {
40292                 return this.dialCodeHolder.dom.value;
40293             }
40294             
40295             var dialCode = "";
40296             if (v.charAt(0) != "+") {
40297                 return false;
40298             }
40299             var numericChars = "";
40300             for (var i = 1; i < v.length; i++) {
40301               var c = v.charAt(i);
40302               if (!isNaN(c)) {
40303                 numericChars += c;
40304                 if (this.dialCodeMapping[numericChars]) {
40305                   dialCode = v.substr(1, i);
40306                 }
40307                 if (numericChars.length == 4) {
40308                   break;
40309                 }
40310               }
40311             }
40312             return dialCode;
40313         },
40314         
40315         reset : function()
40316         {
40317             this.setValue(this.defaultDialCode);
40318             this.validate();
40319         },
40320         
40321         hiddenEl : function()
40322         {
40323             return this.el.select('input.hidden-tel-input',true).first();
40324         },
40325         
40326         onKeyUp : function(e){
40327             
40328             var k = e.getKey();
40329             var c = e.getCharCode();
40330             
40331             if(
40332                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40333                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40334             ){
40335                 e.stopEvent();
40336             }
40337             
40338             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40339             //     return;
40340             // }
40341             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40342                 e.stopEvent();
40343             }
40344             
40345             this.setValue(this.getValue());
40346         }
40347         
40348 });
40349 /**
40350  * @class Roo.bootstrap.MoneyField
40351  * @extends Roo.bootstrap.ComboBox
40352  * Bootstrap MoneyField class
40353  * 
40354  * @constructor
40355  * Create a new MoneyField.
40356  * @param {Object} config Configuration options
40357  */
40358
40359 Roo.bootstrap.MoneyField = function(config) {
40360     
40361     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40362     
40363 };
40364
40365 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40366     
40367     /**
40368      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40369      */
40370     allowDecimals : true,
40371     /**
40372      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40373      */
40374     decimalSeparator : ".",
40375     /**
40376      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40377      */
40378     decimalPrecision : 0,
40379     /**
40380      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40381      */
40382     allowNegative : true,
40383     /**
40384      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40385      */
40386     allowZero: true,
40387     /**
40388      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40389      */
40390     minValue : Number.NEGATIVE_INFINITY,
40391     /**
40392      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40393      */
40394     maxValue : Number.MAX_VALUE,
40395     /**
40396      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40397      */
40398     minText : "The minimum value for this field is {0}",
40399     /**
40400      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40401      */
40402     maxText : "The maximum value for this field is {0}",
40403     /**
40404      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40405      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40406      */
40407     nanText : "{0} is not a valid number",
40408     /**
40409      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40410      */
40411     castInt : true,
40412     /**
40413      * @cfg {String} defaults currency of the MoneyField
40414      * value should be in lkey
40415      */
40416     defaultCurrency : false,
40417     /**
40418      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40419      */
40420     thousandsDelimiter : false,
40421     
40422     
40423     inputlg : 9,
40424     inputmd : 9,
40425     inputsm : 9,
40426     inputxs : 6,
40427     
40428     store : false,
40429     
40430     getAutoCreate : function()
40431     {
40432         var align = this.labelAlign || this.parentLabelAlign();
40433         
40434         var id = Roo.id();
40435
40436         var cfg = {
40437             cls: 'form-group',
40438             cn: []
40439         };
40440
40441         var input =  {
40442             tag: 'input',
40443             id : id,
40444             cls : 'form-control roo-money-amount-input',
40445             autocomplete: 'new-password'
40446         };
40447         
40448         var hiddenInput = {
40449             tag: 'input',
40450             type: 'hidden',
40451             id: Roo.id(),
40452             cls: 'hidden-number-input'
40453         };
40454         
40455         if (this.name) {
40456             hiddenInput.name = this.name;
40457         }
40458
40459         if (this.disabled) {
40460             input.disabled = true;
40461         }
40462
40463         var clg = 12 - this.inputlg;
40464         var cmd = 12 - this.inputmd;
40465         var csm = 12 - this.inputsm;
40466         var cxs = 12 - this.inputxs;
40467         
40468         var container = {
40469             tag : 'div',
40470             cls : 'row roo-money-field',
40471             cn : [
40472                 {
40473                     tag : 'div',
40474                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40475                     cn : [
40476                         {
40477                             tag : 'div',
40478                             cls: 'roo-select2-container input-group',
40479                             cn: [
40480                                 {
40481                                     tag : 'input',
40482                                     cls : 'form-control roo-money-currency-input',
40483                                     autocomplete: 'new-password',
40484                                     readOnly : 1,
40485                                     name : this.currencyName
40486                                 },
40487                                 {
40488                                     tag :'span',
40489                                     cls : 'input-group-addon',
40490                                     cn : [
40491                                         {
40492                                             tag: 'span',
40493                                             cls: 'caret'
40494                                         }
40495                                     ]
40496                                 }
40497                             ]
40498                         }
40499                     ]
40500                 },
40501                 {
40502                     tag : 'div',
40503                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40504                     cn : [
40505                         {
40506                             tag: 'div',
40507                             cls: this.hasFeedback ? 'has-feedback' : '',
40508                             cn: [
40509                                 input
40510                             ]
40511                         }
40512                     ]
40513                 }
40514             ]
40515             
40516         };
40517         
40518         if (this.fieldLabel.length) {
40519             var indicator = {
40520                 tag: 'i',
40521                 tooltip: 'This field is required'
40522             };
40523
40524             var label = {
40525                 tag: 'label',
40526                 'for':  id,
40527                 cls: 'control-label',
40528                 cn: []
40529             };
40530
40531             var label_text = {
40532                 tag: 'span',
40533                 html: this.fieldLabel
40534             };
40535
40536             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40537             label.cn = [
40538                 indicator,
40539                 label_text
40540             ];
40541
40542             if(this.indicatorpos == 'right') {
40543                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40544                 label.cn = [
40545                     label_text,
40546                     indicator
40547                 ];
40548             }
40549
40550             if(align == 'left') {
40551                 container = {
40552                     tag: 'div',
40553                     cn: [
40554                         container
40555                     ]
40556                 };
40557
40558                 if(this.labelWidth > 12){
40559                     label.style = "width: " + this.labelWidth + 'px';
40560                 }
40561                 if(this.labelWidth < 13 && this.labelmd == 0){
40562                     this.labelmd = this.labelWidth;
40563                 }
40564                 if(this.labellg > 0){
40565                     label.cls += ' col-lg-' + this.labellg;
40566                     input.cls += ' col-lg-' + (12 - this.labellg);
40567                 }
40568                 if(this.labelmd > 0){
40569                     label.cls += ' col-md-' + this.labelmd;
40570                     container.cls += ' col-md-' + (12 - this.labelmd);
40571                 }
40572                 if(this.labelsm > 0){
40573                     label.cls += ' col-sm-' + this.labelsm;
40574                     container.cls += ' col-sm-' + (12 - this.labelsm);
40575                 }
40576                 if(this.labelxs > 0){
40577                     label.cls += ' col-xs-' + this.labelxs;
40578                     container.cls += ' col-xs-' + (12 - this.labelxs);
40579                 }
40580             }
40581         }
40582
40583         cfg.cn = [
40584             label,
40585             container,
40586             hiddenInput
40587         ];
40588         
40589         var settings = this;
40590
40591         ['xs','sm','md','lg'].map(function(size){
40592             if (settings[size]) {
40593                 cfg.cls += ' col-' + size + '-' + settings[size];
40594             }
40595         });
40596         
40597         return cfg;
40598     },
40599     
40600     initEvents : function()
40601     {
40602         this.indicator = this.indicatorEl();
40603         
40604         this.initCurrencyEvent();
40605         
40606         this.initNumberEvent();
40607     },
40608     
40609     initCurrencyEvent : function()
40610     {
40611         if (!this.store) {
40612             throw "can not find store for combo";
40613         }
40614         
40615         this.store = Roo.factory(this.store, Roo.data);
40616         this.store.parent = this;
40617         
40618         this.createList();
40619         
40620         this.triggerEl = this.el.select('.input-group-addon', true).first();
40621         
40622         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40623         
40624         var _this = this;
40625         
40626         (function(){
40627             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40628             _this.list.setWidth(lw);
40629         }).defer(100);
40630         
40631         this.list.on('mouseover', this.onViewOver, this);
40632         this.list.on('mousemove', this.onViewMove, this);
40633         this.list.on('scroll', this.onViewScroll, this);
40634         
40635         if(!this.tpl){
40636             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40637         }
40638         
40639         this.view = new Roo.View(this.list, this.tpl, {
40640             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40641         });
40642         
40643         this.view.on('click', this.onViewClick, this);
40644         
40645         this.store.on('beforeload', this.onBeforeLoad, this);
40646         this.store.on('load', this.onLoad, this);
40647         this.store.on('loadexception', this.onLoadException, this);
40648         
40649         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40650             "up" : function(e){
40651                 this.inKeyMode = true;
40652                 this.selectPrev();
40653             },
40654
40655             "down" : function(e){
40656                 if(!this.isExpanded()){
40657                     this.onTriggerClick();
40658                 }else{
40659                     this.inKeyMode = true;
40660                     this.selectNext();
40661                 }
40662             },
40663
40664             "enter" : function(e){
40665                 this.collapse();
40666                 
40667                 if(this.fireEvent("specialkey", this, e)){
40668                     this.onViewClick(false);
40669                 }
40670                 
40671                 return true;
40672             },
40673
40674             "esc" : function(e){
40675                 this.collapse();
40676             },
40677
40678             "tab" : function(e){
40679                 this.collapse();
40680                 
40681                 if(this.fireEvent("specialkey", this, e)){
40682                     this.onViewClick(false);
40683                 }
40684                 
40685                 return true;
40686             },
40687
40688             scope : this,
40689
40690             doRelay : function(foo, bar, hname){
40691                 if(hname == 'down' || this.scope.isExpanded()){
40692                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40693                 }
40694                 return true;
40695             },
40696
40697             forceKeyDown: true
40698         });
40699         
40700         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40701         
40702     },
40703     
40704     initNumberEvent : function(e)
40705     {
40706         this.inputEl().on("keydown" , this.fireKey,  this);
40707         this.inputEl().on("focus", this.onFocus,  this);
40708         this.inputEl().on("blur", this.onBlur,  this);
40709         
40710         this.inputEl().relayEvent('keyup', this);
40711         
40712         if(this.indicator){
40713             this.indicator.addClass('invisible');
40714         }
40715  
40716         this.originalValue = this.getValue();
40717         
40718         if(this.validationEvent == 'keyup'){
40719             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40720             this.inputEl().on('keyup', this.filterValidation, this);
40721         }
40722         else if(this.validationEvent !== false){
40723             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40724         }
40725         
40726         if(this.selectOnFocus){
40727             this.on("focus", this.preFocus, this);
40728             
40729         }
40730         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40731             this.inputEl().on("keypress", this.filterKeys, this);
40732         } else {
40733             this.inputEl().relayEvent('keypress', this);
40734         }
40735         
40736         var allowed = "0123456789";
40737         
40738         if(this.allowDecimals){
40739             allowed += this.decimalSeparator;
40740         }
40741         
40742         if(this.allowNegative){
40743             allowed += "-";
40744         }
40745         
40746         if(this.thousandsDelimiter) {
40747             allowed += ",";
40748         }
40749         
40750         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40751         
40752         var keyPress = function(e){
40753             
40754             var k = e.getKey();
40755             
40756             var c = e.getCharCode();
40757             
40758             if(
40759                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40760                     allowed.indexOf(String.fromCharCode(c)) === -1
40761             ){
40762                 e.stopEvent();
40763                 return;
40764             }
40765             
40766             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40767                 return;
40768             }
40769             
40770             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40771                 e.stopEvent();
40772             }
40773         };
40774         
40775         this.inputEl().on("keypress", keyPress, this);
40776         
40777     },
40778     
40779     onTriggerClick : function(e)
40780     {   
40781         if(this.disabled){
40782             return;
40783         }
40784         
40785         this.page = 0;
40786         this.loadNext = false;
40787         
40788         if(this.isExpanded()){
40789             this.collapse();
40790             return;
40791         }
40792         
40793         this.hasFocus = true;
40794         
40795         if(this.triggerAction == 'all') {
40796             this.doQuery(this.allQuery, true);
40797             return;
40798         }
40799         
40800         this.doQuery(this.getRawValue());
40801     },
40802     
40803     getCurrency : function()
40804     {   
40805         var v = this.currencyEl().getValue();
40806         
40807         return v;
40808     },
40809     
40810     restrictHeight : function()
40811     {
40812         this.list.alignTo(this.currencyEl(), this.listAlign);
40813         this.list.alignTo(this.currencyEl(), this.listAlign);
40814     },
40815     
40816     onViewClick : function(view, doFocus, el, e)
40817     {
40818         var index = this.view.getSelectedIndexes()[0];
40819         
40820         var r = this.store.getAt(index);
40821         
40822         if(r){
40823             this.onSelect(r, index);
40824         }
40825     },
40826     
40827     onSelect : function(record, index){
40828         
40829         if(this.fireEvent('beforeselect', this, record, index) !== false){
40830         
40831             this.setFromCurrencyData(index > -1 ? record.data : false);
40832             
40833             this.collapse();
40834             
40835             this.fireEvent('select', this, record, index);
40836         }
40837     },
40838     
40839     setFromCurrencyData : function(o)
40840     {
40841         var currency = '';
40842         
40843         this.lastCurrency = o;
40844         
40845         if (this.currencyField) {
40846             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40847         } else {
40848             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40849         }
40850         
40851         this.lastSelectionText = currency;
40852         
40853         //setting default currency
40854         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40855             this.setCurrency(this.defaultCurrency);
40856             return;
40857         }
40858         
40859         this.setCurrency(currency);
40860     },
40861     
40862     setFromData : function(o)
40863     {
40864         var c = {};
40865         
40866         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40867         
40868         this.setFromCurrencyData(c);
40869         
40870         var value = '';
40871         
40872         if (this.name) {
40873             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40874         } else {
40875             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40876         }
40877         
40878         this.setValue(value);
40879         
40880     },
40881     
40882     setCurrency : function(v)
40883     {   
40884         this.currencyValue = v;
40885         
40886         if(this.rendered){
40887             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40888             this.validate();
40889         }
40890     },
40891     
40892     setValue : function(v)
40893     {
40894         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40895         
40896         this.value = v;
40897         
40898         if(this.rendered){
40899             
40900             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40901             
40902             this.inputEl().dom.value = (v == '') ? '' :
40903                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40904             
40905             if(!this.allowZero && v === '0') {
40906                 this.hiddenEl().dom.value = '';
40907                 this.inputEl().dom.value = '';
40908             }
40909             
40910             this.validate();
40911         }
40912     },
40913     
40914     getRawValue : function()
40915     {
40916         var v = this.inputEl().getValue();
40917         
40918         return v;
40919     },
40920     
40921     getValue : function()
40922     {
40923         return this.fixPrecision(this.parseValue(this.getRawValue()));
40924     },
40925     
40926     parseValue : function(value)
40927     {
40928         if(this.thousandsDelimiter) {
40929             value += "";
40930             r = new RegExp(",", "g");
40931             value = value.replace(r, "");
40932         }
40933         
40934         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40935         return isNaN(value) ? '' : value;
40936         
40937     },
40938     
40939     fixPrecision : function(value)
40940     {
40941         if(this.thousandsDelimiter) {
40942             value += "";
40943             r = new RegExp(",", "g");
40944             value = value.replace(r, "");
40945         }
40946         
40947         var nan = isNaN(value);
40948         
40949         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40950             return nan ? '' : value;
40951         }
40952         return parseFloat(value).toFixed(this.decimalPrecision);
40953     },
40954     
40955     decimalPrecisionFcn : function(v)
40956     {
40957         return Math.floor(v);
40958     },
40959     
40960     validateValue : function(value)
40961     {
40962         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40963             return false;
40964         }
40965         
40966         var num = this.parseValue(value);
40967         
40968         if(isNaN(num)){
40969             this.markInvalid(String.format(this.nanText, value));
40970             return false;
40971         }
40972         
40973         if(num < this.minValue){
40974             this.markInvalid(String.format(this.minText, this.minValue));
40975             return false;
40976         }
40977         
40978         if(num > this.maxValue){
40979             this.markInvalid(String.format(this.maxText, this.maxValue));
40980             return false;
40981         }
40982         
40983         return true;
40984     },
40985     
40986     validate : function()
40987     {
40988         if(this.disabled || this.allowBlank){
40989             this.markValid();
40990             return true;
40991         }
40992         
40993         var currency = this.getCurrency();
40994         
40995         if(this.validateValue(this.getRawValue()) && currency.length){
40996             this.markValid();
40997             return true;
40998         }
40999         
41000         this.markInvalid();
41001         return false;
41002     },
41003     
41004     getName: function()
41005     {
41006         return this.name;
41007     },
41008     
41009     beforeBlur : function()
41010     {
41011         if(!this.castInt){
41012             return;
41013         }
41014         
41015         var v = this.parseValue(this.getRawValue());
41016         
41017         if(v || v == 0){
41018             this.setValue(v);
41019         }
41020     },
41021     
41022     onBlur : function()
41023     {
41024         this.beforeBlur();
41025         
41026         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41027             //this.el.removeClass(this.focusClass);
41028         }
41029         
41030         this.hasFocus = false;
41031         
41032         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41033             this.validate();
41034         }
41035         
41036         var v = this.getValue();
41037         
41038         if(String(v) !== String(this.startValue)){
41039             this.fireEvent('change', this, v, this.startValue);
41040         }
41041         
41042         this.fireEvent("blur", this);
41043     },
41044     
41045     inputEl : function()
41046     {
41047         return this.el.select('.roo-money-amount-input', true).first();
41048     },
41049     
41050     currencyEl : function()
41051     {
41052         return this.el.select('.roo-money-currency-input', true).first();
41053     },
41054     
41055     hiddenEl : function()
41056     {
41057         return this.el.select('input.hidden-number-input',true).first();
41058     }
41059     
41060 });